twitter-text 1.4.17 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|