twitter-text 1.4.17 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +4 -0
- data/README.rdoc +3 -13
- data/Rakefile +1 -0
- data/lib/twitter-text/autolink.rb +436 -0
- data/lib/twitter-text/deprecation.rb +15 -0
- data/lib/{extractor.rb → twitter-text/extractor.rb} +125 -41
- data/lib/{hithighlighter.rb → twitter-text/hit_highlighter.rb} +5 -7
- data/lib/{regex.rb → twitter-text/regex.rb} +33 -23
- data/lib/twitter-text/rewriter.rb +59 -0
- data/lib/{unicode.rb → twitter-text/unicode.rb} +0 -0
- data/lib/{validation.rb → twitter-text/validation.rb} +17 -3
- data/lib/twitter-text.rb +13 -7
- data/spec/autolinking_spec.rb +192 -16
- data/spec/extractor_spec.rb +12 -0
- data/spec/rewriter_spec.rb +2 -11
- data/spec/spec_helper.rb +1 -1
- data/test/conformance_test.rb +128 -129
- data/twitter-text.gemspec +1 -1
- metadata +14 -12
- data/lib/autolink.rb +0 -266
- data/lib/rewriter.rb +0 -65
data/spec/autolinking_spec.rb
CHANGED
@@ -309,7 +309,7 @@ describe Twitter::Autolink do
|
|
309
309
|
end
|
310
310
|
|
311
311
|
it "should be linked" do
|
312
|
-
@autolinked_text.should == "<a href=\"https://twitter.com/#!/search?q=%23éhashtag\"
|
312
|
+
@autolinked_text.should == "<a class=\"tweet-url hashtag\" href=\"https://twitter.com/#!/search?q=%23éhashtag\" rel=\"nofollow\" title=\"#éhashtag\">#éhashtag</a>"
|
313
313
|
end
|
314
314
|
end
|
315
315
|
|
@@ -424,10 +424,10 @@ describe Twitter::Autolink do
|
|
424
424
|
end
|
425
425
|
|
426
426
|
context "with a URL preceded in forbidden characters" do
|
427
|
-
it "should
|
427
|
+
it "should be linked" do
|
428
428
|
matcher = TestAutolink.new
|
429
429
|
%w| \ ' / ! = |.each do |char|
|
430
|
-
matcher.auto_link("#{char}#{url}").
|
430
|
+
matcher.auto_link("#{char}#{url}").should have_autolinked_url(url)
|
431
431
|
end
|
432
432
|
end
|
433
433
|
end
|
@@ -541,13 +541,23 @@ describe Twitter::Autolink do
|
|
541
541
|
auto_linked.should_not include('hashtag_classname')
|
542
542
|
end
|
543
543
|
|
544
|
+
it "should autolink url/hashtag/mention in text with Unicode supplementary characters" do
|
545
|
+
auto_linked = @linker.auto_link("#{[0x10400].pack('U')} #hashtag #{[0x10400].pack('U')} @mention #{[0x10400].pack('U')} http://twitter.com/")
|
546
|
+
auto_linked.should have_autolinked_hashtag('#hashtag')
|
547
|
+
auto_linked.should link_to_screen_name('mention')
|
548
|
+
auto_linked.should have_autolinked_url('http://twitter.com/')
|
549
|
+
end
|
544
550
|
end
|
545
551
|
|
546
552
|
end
|
547
553
|
|
548
554
|
describe "autolinking options" do
|
555
|
+
before do
|
556
|
+
@linker = TestAutolink.new
|
557
|
+
end
|
558
|
+
|
549
559
|
it "should show display_url when :url_entities provided" do
|
550
|
-
linked =
|
560
|
+
linked = @linker.auto_link("http://t.co/0JG5Mcq", :url_entities => [{
|
551
561
|
"url" => "http://t.co/0JG5Mcq",
|
552
562
|
"display_url" => "blog.twitter.com/2011/05/twitte…",
|
553
563
|
"expanded_url" => "http://blog.twitter.com/2011/05/twitter-for-mac-update.html",
|
@@ -561,67 +571,233 @@ describe Twitter::Autolink do
|
|
561
571
|
html.search('a[@href="http://t.co/0JG5Mcq"]').should_not be_empty
|
562
572
|
html.search('span[@class=js-display-url]').inner_text.should == "blog.twitter.com/2011/05/twitte"
|
563
573
|
html.inner_text.should == " http://blog.twitter.com/2011/05/twitter-for-mac-update.html …"
|
574
|
+
html.search('span[@style="position:absolute;left:-9999px;"]').size.should == 4
|
575
|
+
end
|
576
|
+
|
577
|
+
it "should accept invisible_tag_attrs option" do
|
578
|
+
linked = @linker.auto_link("http://t.co/0JG5Mcq",
|
579
|
+
{
|
580
|
+
:url_entities => [{
|
581
|
+
"url" => "http://t.co/0JG5Mcq",
|
582
|
+
"display_url" => "blog.twitter.com/2011/05/twitte…",
|
583
|
+
"expanded_url" => "http://blog.twitter.com/2011/05/twitter-for-mac-update.html",
|
584
|
+
"indices" => [
|
585
|
+
0,
|
586
|
+
19
|
587
|
+
]
|
588
|
+
}],
|
589
|
+
:invisible_tag_attrs => "style='dummy;'"
|
590
|
+
})
|
591
|
+
html = Nokogiri::HTML(linked)
|
592
|
+
html.search('span[@style="dummy;"]').size.should == 4
|
593
|
+
end
|
594
|
+
|
595
|
+
it "should show display_url if available in entity" do
|
596
|
+
linked = @linker.auto_link_entities("http://t.co/0JG5Mcq",
|
597
|
+
[{
|
598
|
+
:url => "http://t.co/0JG5Mcq",
|
599
|
+
:display_url => "blog.twitter.com/2011/05/twitte…",
|
600
|
+
:expanded_url => "http://blog.twitter.com/2011/05/twitter-for-mac-update.html",
|
601
|
+
:indices => [0, 19]
|
602
|
+
}]
|
603
|
+
)
|
604
|
+
html = Nokogiri::HTML(linked)
|
605
|
+
html.search('a').should_not be_empty
|
606
|
+
html.search('a[@href="http://t.co/0JG5Mcq"]').should_not be_empty
|
607
|
+
html.search('span[@class=js-display-url]').inner_text.should == "blog.twitter.com/2011/05/twitte"
|
608
|
+
html.inner_text.should == " http://blog.twitter.com/2011/05/twitter-for-mac-update.html …"
|
564
609
|
end
|
565
610
|
|
566
|
-
it "should apply :
|
567
|
-
linked =
|
611
|
+
it "should apply :class as a CSS class" do
|
612
|
+
linked = @linker.auto_link("http://example.com/", :class => 'myclass')
|
568
613
|
linked.should have_autolinked_url('http://example.com/')
|
569
614
|
linked.should match(/myclass/)
|
570
615
|
end
|
571
616
|
|
617
|
+
it "should apply :url_class only on URL" do
|
618
|
+
linked = @linker.auto_link("http://twitter.com")
|
619
|
+
linked.should have_autolinked_url('http://twitter.com')
|
620
|
+
linked.should_not match(/class/)
|
621
|
+
|
622
|
+
linked = @linker.auto_link("http://twitter.com", :url_class => 'testClass')
|
623
|
+
linked.should have_autolinked_url('http://twitter.com')
|
624
|
+
linked.should match(/class=\"testClass\"/)
|
625
|
+
|
626
|
+
linked = @linker.auto_link("#hash @tw", :url_class => 'testClass')
|
627
|
+
linked.should match(/class=\"tweet-url hashtag\"/)
|
628
|
+
linked.should match(/class=\"tweet-url username\"/)
|
629
|
+
linked.should_not match(/class=\"testClass\"/)
|
630
|
+
end
|
631
|
+
|
572
632
|
it "should add rel=nofollow by default" do
|
573
|
-
linked =
|
633
|
+
linked = @linker.auto_link("http://example.com/")
|
574
634
|
linked.should have_autolinked_url('http://example.com/')
|
575
635
|
linked.should match(/nofollow/)
|
576
636
|
end
|
577
637
|
|
578
638
|
it "should include the '@' symbol in a username when passed :username_include_symbol" do
|
579
|
-
linked =
|
639
|
+
linked = @linker.auto_link("@user", :username_include_symbol => true)
|
580
640
|
linked.should link_to_screen_name('user', '@user')
|
581
641
|
end
|
582
642
|
|
583
643
|
it "should include the '@' symbol in a list when passed :username_include_symbol" do
|
584
|
-
linked =
|
644
|
+
linked = @linker.auto_link("@user/list", :username_include_symbol => true)
|
585
645
|
linked.should link_to_list_path('user/list', '@user/list')
|
586
646
|
end
|
587
647
|
|
588
648
|
it "should not add rel=nofollow when passed :suppress_no_follow" do
|
589
|
-
linked =
|
649
|
+
linked = @linker.auto_link("http://example.com/", :suppress_no_follow => true)
|
590
650
|
linked.should have_autolinked_url('http://example.com/')
|
591
651
|
linked.should_not match(/nofollow/)
|
592
652
|
end
|
593
653
|
|
594
654
|
it "should not add a target attribute by default" do
|
595
|
-
linked =
|
655
|
+
linked = @linker.auto_link("http://example.com/")
|
596
656
|
linked.should have_autolinked_url('http://example.com/')
|
597
657
|
linked.should_not match(/target=/)
|
598
658
|
end
|
599
659
|
|
600
660
|
it "should respect the :target option" do
|
601
|
-
linked =
|
661
|
+
linked = @linker.auto_link("http://example.com/", :target => 'mywindow')
|
602
662
|
linked.should have_autolinked_url('http://example.com/')
|
603
663
|
linked.should match(/target="mywindow"/)
|
604
664
|
end
|
605
665
|
|
606
666
|
it "should customize href by username_url_block option" do
|
607
|
-
linked =
|
667
|
+
linked = @linker.auto_link("@test", :username_url_block => lambda{|a| "dummy"})
|
608
668
|
linked.should have_autolinked_url('dummy', 'test')
|
609
669
|
end
|
610
670
|
|
611
671
|
it "should customize href by list_url_block option" do
|
612
|
-
linked =
|
672
|
+
linked = @linker.auto_link("@test/list", :list_url_block => lambda{|a| "dummy"})
|
613
673
|
linked.should have_autolinked_url('dummy', 'test/list')
|
614
674
|
end
|
615
675
|
|
616
676
|
it "should customize href by hashtag_url_block option" do
|
617
|
-
linked =
|
677
|
+
linked = @linker.auto_link("#hashtag", :hashtag_url_block => lambda{|a| "dummy"})
|
618
678
|
linked.should have_autolinked_url('dummy', '#hashtag')
|
619
679
|
end
|
620
680
|
|
621
681
|
it "should customize href by link_url_block option" do
|
622
|
-
linked =
|
682
|
+
linked = @linker.auto_link("http://example.com/", :link_url_block => lambda{|a| "dummy"})
|
623
683
|
linked.should have_autolinked_url('dummy', 'http://example.com/')
|
624
684
|
end
|
685
|
+
|
686
|
+
it "should modify link attributes by link_attribute_block" do
|
687
|
+
linked = @linker.auto_link("#hash @mention",
|
688
|
+
:link_attribute_block => lambda{|entity, attributes|
|
689
|
+
attributes[:"dummy-hash-attr"] = "test" if entity[:hashtag]
|
690
|
+
}
|
691
|
+
)
|
692
|
+
linked.should match(/<a[^>]+hashtag[^>]+dummy-hash-attr=\"test\"[^>]+>/)
|
693
|
+
linked.should_not match(/<a[^>]+username[^>]+dummy-hash-attr=\"test\"[^>]+>/)
|
694
|
+
linked.should_not match(/link_attribute_block/i)
|
695
|
+
|
696
|
+
linked = @linker.auto_link("@mention http://twitter.com/",
|
697
|
+
:link_attribute_block => lambda{|entity, attributes|
|
698
|
+
attributes["dummy-url-attr"] = entity[:url] if entity[:url]
|
699
|
+
}
|
700
|
+
)
|
701
|
+
linked.should_not match(/<a[^>]+username[^>]+dummy-url-attr=\"http:\/\/twitter.com\/\"[^>]*>/)
|
702
|
+
linked.should match(/<a[^>]+dummy-url-attr=\"http:\/\/twitter.com\/\"/)
|
703
|
+
end
|
704
|
+
|
705
|
+
it "should modify link text by link_text_block" do
|
706
|
+
linked = @linker.auto_link("#hash @mention",
|
707
|
+
:link_text_block => lambda{|entity, text|
|
708
|
+
entity[:hashtag] ? "#replaced" : "pre_#{text}_post"
|
709
|
+
}
|
710
|
+
)
|
711
|
+
linked.should match(/<a[^>]+>#replaced<\/a>/)
|
712
|
+
linked.should match(/<a[^>]+>pre_mention_post<\/a>/)
|
713
|
+
|
714
|
+
linked = @linker.auto_link("#hash @mention", {
|
715
|
+
:link_text_block => lambda{|entity, text|
|
716
|
+
"pre_#{text}_post"
|
717
|
+
},
|
718
|
+
:symbol_tag => "s", :text_with_symbol_tag => "b", :username_include_symbol => true
|
719
|
+
})
|
720
|
+
linked.should match(/<a[^>]+>pre_<s>#<\/s><b>hash<\/b>_post<\/a>/)
|
721
|
+
linked.should match(/<a[^>]+>pre_<s>@<\/s><b>mention<\/b>_post<\/a>/)
|
722
|
+
end
|
723
|
+
|
724
|
+
it "should apply :url_target only to auto-linked URLs" do
|
725
|
+
auto_linked = @linker.auto_link("#hashtag @mention http://test.com/", {:url_target => '_blank'})
|
726
|
+
auto_linked.should have_autolinked_hashtag('#hashtag')
|
727
|
+
auto_linked.should link_to_screen_name('mention')
|
728
|
+
auto_linked.should have_autolinked_url('http://test.com/')
|
729
|
+
auto_linked.should_not match(/<a[^>]+hashtag[^>]+target[^>]+>/)
|
730
|
+
auto_linked.should_not match(/<a[^>]+username[^>]+target[^>]+>/)
|
731
|
+
auto_linked.should match(/<a[^>]+test.com[^>]+target=\"_blank\"[^>]*>/)
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
describe "link_url_with_entity" do
|
736
|
+
before do
|
737
|
+
@linker = TestAutolink.new
|
738
|
+
end
|
739
|
+
|
740
|
+
it "should use display_url and expanded_url" do
|
741
|
+
@linker.send(:link_url_with_entity,
|
742
|
+
{
|
743
|
+
:url => "http://t.co/abcde",
|
744
|
+
:display_url => "twitter.com",
|
745
|
+
:expanded_url => "http://twitter.com/"},
|
746
|
+
{:invisible_tag_attrs => "class='invisible'"}).gsub('"', "'").should == "<span class='tco-ellipsis'><span class='invisible'> </span></span><span class='invisible'>http://</span><span class='js-display-url'>twitter.com</span><span class='invisible'>/</span><span class='tco-ellipsis'><span class='invisible'> </span></span>";
|
747
|
+
end
|
748
|
+
|
749
|
+
it "should correctly handle display_url ending with '…'" do
|
750
|
+
@linker.send(:link_url_with_entity,
|
751
|
+
{
|
752
|
+
:url => "http://t.co/abcde",
|
753
|
+
:display_url => "twitter.com…",
|
754
|
+
:expanded_url => "http://twitter.com/abcdefg"},
|
755
|
+
{:invisible_tag_attrs => "class='invisible'"}).gsub('"', "'").should == "<span class='tco-ellipsis'><span class='invisible'> </span></span><span class='invisible'>http://</span><span class='js-display-url'>twitter.com</span><span class='invisible'>/abcdefg</span><span class='tco-ellipsis'><span class='invisible'> </span>…</span>";
|
756
|
+
end
|
757
|
+
|
758
|
+
it "should correctly handle display_url starting with '…'" do
|
759
|
+
@linker.send(:link_url_with_entity,
|
760
|
+
{
|
761
|
+
:url => "http://t.co/abcde",
|
762
|
+
:display_url => "…tter.com/abcdefg",
|
763
|
+
:expanded_url => "http://twitter.com/abcdefg"},
|
764
|
+
{:invisible_tag_attrs => "class='invisible'"}).gsub('"', "'").should == "<span class='tco-ellipsis'>…<span class='invisible'> </span></span><span class='invisible'>http://twi</span><span class='js-display-url'>tter.com/abcdefg</span><span class='invisible'></span><span class='tco-ellipsis'><span class='invisible'> </span></span>";
|
765
|
+
end
|
766
|
+
|
767
|
+
it "should not create spans if display_url and expanded_url are on different domains" do
|
768
|
+
@linker.send(:link_url_with_entity,
|
769
|
+
{
|
770
|
+
:url => "http://t.co/abcde",
|
771
|
+
:display_url => "pic.twitter.com/xyz",
|
772
|
+
:expanded_url => "http://twitter.com/foo/statuses/123/photo/1"},
|
773
|
+
{:invisible_tag_attrs => "class='invisible'"}).gsub('"', "'").should == "pic.twitter.com/xyz"
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
describe "symbol_tag" do
|
778
|
+
before do
|
779
|
+
@linker = TestAutolink.new
|
780
|
+
end
|
781
|
+
it "should put :symbol_tag around symbol" do
|
782
|
+
@linker.auto_link("@mention", {:symbol_tag => 's', :username_include_symbol=>true}).should match(/<s>@<\/s>mention/)
|
783
|
+
@linker.auto_link("#hash", {:symbol_tag => 's'}).should match(/<s>#<\/s>hash/)
|
784
|
+
result = @linker.auto_link("@mention #hash $CASH", {:symbol_tag => 'b', :username_include_symbol=>true})
|
785
|
+
result.should match(/<b>@<\/b>mention/)
|
786
|
+
result.should match(/<b>#<\/b>hash/)
|
787
|
+
result.should match(/<b>\$<\/b>CASH/)
|
788
|
+
end
|
789
|
+
it "should put :text_with_symbol_tag around text" do
|
790
|
+
result = @linker.auto_link("@mention #hash $CASH", {:text_with_symbol_tag => 'b'})
|
791
|
+
result.should match(/<b>mention<\/b>/)
|
792
|
+
result.should match(/<b>hash<\/b>/)
|
793
|
+
result.should match(/<b>CASH<\/b>/)
|
794
|
+
end
|
795
|
+
it "should put :symbol_tag around symbol and :text_with_symbol_tag around text" do
|
796
|
+
result = @linker.auto_link("@mention #hash $CASH", {:symbol_tag => 's', :text_with_symbol_tag => 'b', :username_include_symbol=>true})
|
797
|
+
result.should match(/<s>@<\/s><b>mention<\/b>/)
|
798
|
+
result.should match(/<s>#<\/s><b>hash<\/b>/)
|
799
|
+
result.should match(/<s>\$<\/s><b>CASH<\/b>/)
|
800
|
+
end
|
625
801
|
end
|
626
802
|
|
627
803
|
describe "html_escape" do
|
data/spec/extractor_spec.rb
CHANGED
@@ -107,6 +107,10 @@ describe Twitter::Extractor do
|
|
107
107
|
end
|
108
108
|
needed.should == []
|
109
109
|
end
|
110
|
+
|
111
|
+
it "should extract screen name in text with supplementary character" do
|
112
|
+
@extractor.extract_mentioned_screen_names_with_indices("#{[0x10400].pack('U')} @alice").should == [{:screen_name => "alice", :indices => [2, 8]}]
|
113
|
+
end
|
110
114
|
end
|
111
115
|
|
112
116
|
describe "replies" do
|
@@ -221,6 +225,10 @@ describe Twitter::Extractor do
|
|
221
225
|
extracted_url[:indices].last.should == 11 + url.chars.to_a.size
|
222
226
|
end
|
223
227
|
end
|
228
|
+
|
229
|
+
it "should extract URL in text with supplementary character" do
|
230
|
+
@extractor.extract_urls_with_indices("#{[0x10400].pack('U')} http://twitter.com").should == [{:url => "http://twitter.com", :indices => [2, 20]}]
|
231
|
+
end
|
224
232
|
end
|
225
233
|
|
226
234
|
describe "invalid URLS" do
|
@@ -352,5 +360,9 @@ describe Twitter::Extractor do
|
|
352
360
|
it "should not extract numeric hashtags" do
|
353
361
|
not_match_hashtag_in_text("#1234")
|
354
362
|
end
|
363
|
+
|
364
|
+
it "should extract hashtag in text with supplementary character" do
|
365
|
+
match_hashtag_in_text("hashtag", "#{[0x10400].pack('U')} #hashtag", 2)
|
366
|
+
end
|
355
367
|
end
|
356
368
|
end
|
data/spec/rewriter_spec.rb
CHANGED
@@ -240,15 +240,6 @@ describe Twitter::Rewriter do
|
|
240
240
|
end
|
241
241
|
end
|
242
242
|
|
243
|
-
context "with a page anchor in a url" do
|
244
|
-
def original_text; "Here's my url: http://foobar.com/#home"; end
|
245
|
-
|
246
|
-
it "should not link the hashtag" do
|
247
|
-
@block_args.should be_nil
|
248
|
-
@rewritten_text.should == "Here's my url: http://foobar.com/#home"
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
243
|
context "with a hashtag that starts with a number but has word characters" do
|
253
244
|
def original_text; "#2ab"; end
|
254
245
|
|
@@ -469,11 +460,11 @@ describe Twitter::Rewriter do
|
|
469
460
|
end
|
470
461
|
|
471
462
|
context "with a URL preceded in forbidden characters" do
|
472
|
-
it "should
|
463
|
+
it "should be rewritten" do
|
473
464
|
%w| \ ' / ! = |.each do |char|
|
474
465
|
Twitter::Rewriter.rewrite_urls("#{char}#{url}") do |url|
|
475
466
|
"[rewritten]" # should not be called here.
|
476
|
-
end.should == "#{char}
|
467
|
+
end.should == "#{char}[rewritten]"
|
477
468
|
end
|
478
469
|
end
|
479
470
|
end
|