]> git.wincent.com - wikitext.git/commitdiff
Consistently apply "mailto" class to mailto URIs
authorWincent Colaiuta <win@wincent.com>
Fri, 27 Mar 2009 11:03:35 +0000 (12:03 +0100)
committerWincent Colaiuta <win@wincent.com>
Fri, 27 Mar 2009 11:03:35 +0000 (12:03 +0100)
Implement more consistent CSS styling given mailto URIs.
The following inputs previously received "external" CSS
styling because mailto URIs are tokenized as URIs rather
than MAIL tokens:

  mailto:user@example.com
  [mailto:user@example.com email me]

Only raw email addresses were correctly styled:

  user@example.com

Now all of the above cases consistently use the "mailto"
class.

See:
  https://wincent.com/issues/1262

Signed-off-by: Wincent Colaiuta <win@wincent.com>
ext/parser.c
spec/autolinking_spec.rb
spec/external_link_spec.rb

index 290635ae3e02100a8f3c710f49139a5f231a799b..f4bb04c3634f8f9827a4cd353b69037ba4a44edb 100644 (file)
@@ -31,6 +31,7 @@ typedef struct
     VALUE   link_target;            // short term "memory" for parsing links
     VALUE   link_text;              // short term "memory" for parsing links
     VALUE   external_link_class;    // CSS class applied to external links
+    VALUE   mailto_class;           // CSS class applied to email (mailto) links
     VALUE   img_prefix;             // path prepended when emitting img tags
     ary_t   *scope;                 // stack for tracking scope
     ary_t   *line;                  // stack for tracking scope as implied by current line
@@ -220,12 +221,20 @@ VALUE _Wikitext_downcase(VALUE string)
     return string;
 }
 
-VALUE _Wikitext_hyperlink(VALUE link_prefix, VALUE link_target, VALUE link_text, VALUE link_class)
+VALUE _Wikitext_hyperlink(parser_t *parser, VALUE link_prefix, VALUE link_target, VALUE link_text, VALUE link_class)
 {
     VALUE string = rb_str_new(a_start, sizeof(a_start) - 1);        // <a href="
     if (!NIL_P(link_prefix))
         rb_str_append(string, link_prefix);
     rb_str_append(string, link_target);
+
+    /* special handling for mailto URIs */
+    const char *mailto = "mailto:";
+    if (NIL_P(link_prefix) &&
+        RSTRING_LEN(link_target) >= (long)sizeof(mailto) &&
+        strncmp(mailto, RSTRING_PTR(link_target), sizeof(mailto)) == 0)
+        link_class = parser->mailto_class; // use mailto_class from parser
+
     if (link_class != Qnil)
     {
         rb_str_cat(string, a_class, sizeof(a_class) - 1);           // " class="
@@ -864,7 +873,7 @@ void _Wikitext_rollback_failed_external_link(parser_t *parser)
     if (!NIL_P(parser->link_target))
     {
         if (parser->autolink == Qtrue)
-            parser->link_target = _Wikitext_hyperlink(Qnil, parser->link_target, parser->link_target, parser->external_link_class);
+            parser->link_target = _Wikitext_hyperlink(parser, Qnil, parser->link_target, parser->link_target, parser->external_link_class);
         rb_str_append(parser->output, parser->link_target);
         if (scope_includes_space)
         {
@@ -989,6 +998,7 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self)
     parser->link_target             = Qnil;
     parser->link_text               = Qnil;
     parser->external_link_class     = link_class;
+    parser->mailto_class            = mailto_class;
     parser->img_prefix              = rb_iv_get(self, "@img_prefix");
     parser->scope                   = ary_new();
     GC_WRAP_ARY(parser->scope, scope_gc);
@@ -1884,7 +1894,7 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self)
                     _Wikitext_start_para_if_necessary(parser);
                     i = TOKEN_TEXT(token);
                     if (parser->autolink == Qtrue)
-                        i = _Wikitext_hyperlink(rb_str_new2("mailto:"), i, i, mailto_class);
+                        i = _Wikitext_hyperlink(parser, rb_str_new2("mailto:"), i, i, mailto_class);
                     rb_str_append(parser->output, i);
                 }
                 break;
@@ -1900,7 +1910,7 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self)
                     _Wikitext_rollback_failed_link(parser);
                     i = TOKEN_TEXT(token);
                     if (parser->autolink == Qtrue)
-                        i = _Wikitext_hyperlink(Qnil, i, i, parser->external_link_class); // link target, link text
+                        i = _Wikitext_hyperlink(parser, Qnil, i, i, parser->external_link_class); // link target, link text
                     rb_str_append(parser->output, i);
                 }
                 else if (IN(EXT_LINK_START))
@@ -1926,7 +1936,7 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self)
                             _Wikitext_start_para_if_necessary(parser);
                             rb_str_cat(parser->output, ext_link_start, sizeof(ext_link_start) - 1);
                             if (parser->autolink == Qtrue)
-                                i = _Wikitext_hyperlink(Qnil, i, i, parser->external_link_class); // link target, link text
+                                i = _Wikitext_hyperlink(parser, Qnil, i, i, parser->external_link_class); // link target, link text
                             rb_str_append(parser->output, i);
                         }
                     }
@@ -1947,7 +1957,7 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self)
                     _Wikitext_start_para_if_necessary(parser);
                     i = TOKEN_TEXT(token);
                     if (parser->autolink == Qtrue)
-                        i = _Wikitext_hyperlink(Qnil, i, i, parser->external_link_class); // link target, link text
+                        i = _Wikitext_hyperlink(parser, Qnil, i, i, parser->external_link_class); // link target, link text
                     rb_str_append(parser->output, i);
                 }
                 break;
@@ -2125,7 +2135,7 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self)
                     _Wikitext_parser_encode_link_target(parser);
                     _Wikitext_pop_from_stack_up_to(parser, i, LINK_START, Qtrue);
                     parser->capture     = Qnil;
-                    i = _Wikitext_hyperlink(prefix, parser->link_target, parser->link_text, Qnil);
+                    i = _Wikitext_hyperlink(parser, prefix, parser->link_target, parser->link_text, Qnil);
                     rb_str_append(parser->output, i);
                     parser->link_target = Qnil;
                     parser->link_text   = Qnil;
@@ -2209,7 +2219,7 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self)
                         j = IN(PATH) ? Qnil : parser->external_link_class;
                         _Wikitext_pop_from_stack_up_to(parser, i, EXT_LINK_START, Qtrue);
                         parser->capture = Qnil;
-                        i = _Wikitext_hyperlink(Qnil, parser->link_target, parser->link_text, j);
+                        i = _Wikitext_hyperlink(parser, Qnil, parser->link_target, parser->link_text, j);
                         rb_str_append(parser->output, i);
                     }
                     parser->link_target = Qnil;
index 382a93cc9c4dae38134e3363b7c403fe2dfdf8f8..487a589bb9e1c3d5771a5be135a8f506759e0037 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/env ruby
-# Copyright 2007-2008 Wincent Colaiuta
+# Copyright 2007-2009 Wincent Colaiuta
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -43,7 +43,7 @@ describe Wikitext::Parser, 'autolinking' do
 
     it 'should convert mailto URIs into hyperlinks' do
       uri = 'mailto:user@example.com'
-      @parser.parse(uri).should == %Q{<p><a href="mailto:user@example.com" class="external">mailto:user@example.com</a></p>\n}
+      @parser.parse(uri).should == %Q{<p><a href="mailto:user@example.com" class="mailto">mailto:user@example.com</a></p>\n}
     end
 
     it 'should convert SVN URIs into hyperlinks' do
@@ -81,18 +81,30 @@ describe Wikitext::Parser, 'autolinking' do
       @parser.parse(uri).should == %Q{<p><a href="mailto:user@example.com" class="mailto">user@example.com</a></p>\n}
     end
 
-    it 'should apply the mailto CSS class if set' do
+    it 'should apply the mailto CSS class if set (raw address)' do
       uri = 'user@example.com'
       @parser.mailto_class = 'foo'
       @parser.parse(uri).should == %Q{<p><a href="mailto:user@example.com" class="foo">user@example.com</a></p>\n}
     end
 
-    it 'should apply no CSS if the mailto class is set to nil' do
+    it 'should apply the mailto CSS class if set (mailto URI)' do
+      uri = 'mailto:user@example.com'
+      @parser.mailto_class = 'foo'
+      @parser.parse(uri).should == %Q{<p><a href="mailto:user@example.com" class="foo">mailto:user@example.com</a></p>\n}
+    end
+
+    it 'should apply no CSS if the mailto class is set to nil (raw address)' do
       uri = 'user@example.com'
       @parser.mailto_class = nil
       @parser.parse(uri).should == %Q{<p><a href="mailto:user@example.com">user@example.com</a></p>\n}
     end
 
+    it 'should apply no CSS if the mailto class is set to nil (mailto URI)' do
+      uri = 'mailto:user@example.com'
+      @parser.mailto_class = nil
+      @parser.parse(uri).should == %Q{<p><a href="mailto:user@example.com">mailto:user@example.com</a></p>\n}
+    end
+
     it 'should pass through emails unchanged inside <nowiki></nowiki> spans' do
       @parser.parse("<nowiki>user@example.com</nowiki>").should == "<p>user@example.com</p>\n"  # was a crasher
     end
@@ -476,37 +488,37 @@ describe Wikitext::Parser, 'autolinking' do
     describe 'after "mailto:" URIs' do
       it 'should terminate if followed by a period' do
         input     = 'Try mailto:user@example.com.'
-        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="external">mailto:user@example.com</a>.</p>\n}
+        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="mailto">mailto:user@example.com</a>.</p>\n}
         @parser.parse(input).should == expected
       end
 
       it 'should not terminate on periods that occur within the URI' do
         input     = 'Try mailto:user.name@example.com.'
-        expected  = %Q{<p>Try <a href="mailto:user.name@example.com" class="external">mailto:user.name@example.com</a>.</p>\n}
+        expected  = %Q{<p>Try <a href="mailto:user.name@example.com" class="mailto">mailto:user.name@example.com</a>.</p>\n}
         @parser.parse(input).should == expected
       end
 
       it 'should terminate if followed by a colon' do
         input     = 'Try mailto:user@example.com:'
-        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="external">mailto:user@example.com</a>:</p>\n}
+        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="mailto">mailto:user@example.com</a>:</p>\n}
         @parser.parse(input).should == expected
       end
 
       it 'should terminate if followed by a comma' do
         input     = 'Try mailto:user@example.com,'
-        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="external">mailto:user@example.com</a>,</p>\n}
+        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="mailto">mailto:user@example.com</a>,</p>\n}
         @parser.parse(input).should == expected
       end
 
       it 'should terminate if followed by an exclamation mark' do
         input     = 'Try mailto:user@example.com!'
-        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="external">mailto:user@example.com</a>!</p>\n}
+        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="mailto">mailto:user@example.com</a>!</p>\n}
         @parser.parse(input).should == expected
       end
 
       it 'should terminate if followed by a question mark' do
         input     = 'Try mailto:user@example.com?'
-        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="external">mailto:user@example.com</a>?</p>\n}
+        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="mailto">mailto:user@example.com</a>?</p>\n}
         @parser.parse(input).should == expected
       end
 
@@ -520,13 +532,13 @@ describe Wikitext::Parser, 'autolinking' do
 
       it 'should terminate if followed by a semi-colon' do
         input     = 'Try mailto:user@example.com;'
-        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="external">mailto:user@example.com</a>;</p>\n}
+        expected  = %Q{<p>Try <a href="mailto:user@example.com" class="mailto">mailto:user@example.com</a>;</p>\n}
         @parser.parse(input).should == expected
       end
 
       it 'should terminate if followed by a right parenthesis' do
         input     = '(Try mailto:user@example.com)'
-        expected  = %Q{<p>(Try <a href="mailto:user@example.com" class="external">mailto:user@example.com</a>)</p>\n}
+        expected  = %Q{<p>(Try <a href="mailto:user@example.com" class="mailto">mailto:user@example.com</a>)</p>\n}
         @parser.parse(input).should == expected
       end
     end
index caba9fa568e962e026458b54f2e8ac4142a581bb..275d6791f2f88bee56702189c139564af442ea66 100755 (executable)
@@ -43,12 +43,15 @@ describe Wikitext::Parser, 'external links' do
   end
 
   it 'should format valid external mailto links' do
-    # although note in this case the CSS class is "external" rather than "mailto"
-    # this is because we're matching this as a (generic) URI rather than an email address
-    expected = %Q{<p><a href="mailto:user@example.com" class="external">john</a></p>\n}
+    expected = %Q{<p><a href="mailto:user@example.com" class="mailto">john</a></p>\n}
     @parser.parse('[mailto:user@example.com john]').should == expected
   end
 
+  it 'should not treat raw email addresses as valid link targets' do
+    expected = %Q{<p>[<a href="mailto:user@example.com" class="mailto">user@example.com</a> john]</p>\n}
+    @parser.parse('[user@example.com john]').should == expected
+  end
+
   it 'should format absolute path links' do
     expected = %Q{<p><a href="/foo/bar">fb</a></p>\n} # note no "external" class
     @parser.parse('[/foo/bar fb]').should == expected