From 550ef104c6f4b9d656c030dcf6e10d1d31ae68bb Mon Sep 17 00:00:00 2001
From: Robin Ward <robin.ward@gmail.com>
Date: Wed, 28 Aug 2013 13:06:41 -0400
Subject: [PATCH] FIX: Markdown references within a list were not working
 properly.

---
 .../javascripts/external/better_markdown.js   | 73 ++++++++++++-------
 test/javascripts/components/markdown_test.js  |  4 +
 2 files changed, 51 insertions(+), 26 deletions(-)

diff --git a/app/assets/javascripts/external/better_markdown.js b/app/assets/javascripts/external/better_markdown.js
index 29e708fb857..dd46e8782b1 100644
--- a/app/assets/javascripts/external/better_markdown.js
+++ b/app/assets/javascripts/external/better_markdown.js
@@ -8,6 +8,8 @@
 
   * We don't escape the contents of HTML as we prefer to use a whitelist.
 
+  * We fixed a bug where references can be created directly following a list.
+
   * Note the name BetterMarkdown doesn't mean it's *better* than markdown-js, it refers
     to it being better than our previous markdown parser!
 
@@ -205,6 +207,35 @@ Markdown.prototype.split_blocks = function splitBlocks( input, startLine ) {
   return blocks;
 };
 
+function create_attrs() {
+  if ( !extract_attr( this.tree ) ) {
+    this.tree.splice( 1, 0, {} );
+  }
+
+  var attrs = extract_attr( this.tree );
+
+  // make a references hash if it doesn't exist
+  if ( attrs.references === undefined ) {
+    attrs.references = {};
+  }
+
+  return attrs;
+}
+
+function create_reference(attrs, m) {
+  if ( m[2] && m[2][0] == "<" && m[2][m[2].length-1] == ">" )
+    m[2] = m[2].substring( 1, m[2].length - 1 );
+
+  var ref = attrs.references[ m[1].toLowerCase() ] = {
+    href: m[2]
+  };
+
+  if ( m[4] !== undefined )
+    ref.title = m[4];
+  else if ( m[5] !== undefined )
+    ref.title = m[5];
+}
+
 /**
  *  Markdown#processBlock( block, next ) -> undefined | [ JsonML, ... ]
  *  - block (String): the block to process
@@ -531,6 +562,7 @@ Markdown.dialects.Gruber = {
 
       // The matcher function
       return function( block, next ) {
+
         var m = block.match( is_list_re );
         if ( !m ) return undefined;
 
@@ -682,6 +714,7 @@ Markdown.dialects.Gruber = {
     })(),
 
     blockquote: function blockquote( block, next ) {
+
       if ( !block.match( /^>/m ) )
         return undefined;
 
@@ -736,39 +769,18 @@ Markdown.dialects.Gruber = {
     },
 
     referenceDefn: function referenceDefn( block, next) {
+
       var re = /^\s*\[(.*?)\]:\s*(\S+)(?:\s+(?:(['"])(.*?)\3|\((.*?)\)))?\n?/;
       // interesting matches are [ , ref_id, url, , title, title ]
 
       if ( !block.match(re) )
         return undefined;
 
-      // make an attribute node if it doesn't exist
-      if ( !extract_attr( this.tree ) ) {
-        this.tree.splice( 1, 0, {} );
-      }
-
-      var attrs = extract_attr( this.tree );
-
-      // make a references hash if it doesn't exist
-      if ( attrs.references === undefined ) {
-        attrs.references = {};
-      }
+      var attrs = create_attrs.call(this);
 
       var b = this.loop_re_over_block(re, block, function( m ) {
-
-        if ( m[2] && m[2][0] == "<" && m[2][m[2].length-1] == ">" )
-          m[2] = m[2].substring( 1, m[2].length - 1 );
-
-        var ref = attrs.references[ m[1].toLowerCase() ] = {
-          href: m[2]
-        };
-
-        if ( m[4] !== undefined )
-          ref.title = m[4];
-        else if ( m[5] !== undefined )
-          ref.title = m[5];
-
-      } );
+        create_reference(attrs, m);
+      });
 
       if ( b.length )
         next.unshift( mk_block( b, block.trailing ) );
@@ -891,6 +903,7 @@ Markdown.dialects.Gruber.inline = {
     "[": function link( text ) {
 
       var orig = String(text);
+
       // Inline content is possible inside `link text`
       var res = Markdown.DialectHelpers.inline_until_char.call( this, text.substr(1), "]" );
 
@@ -954,7 +967,6 @@ Markdown.dialects.Gruber.inline = {
       m = text.match( /^\s*\[(.*?)\]/ );
 
       if ( m ) {
-
         consumed += m[ 0 ].length;
 
         // [links][] uses links as its reference
@@ -968,6 +980,15 @@ Markdown.dialects.Gruber.inline = {
         return [ consumed, link ];
       }
 
+      m = orig.match(/^\s*\[(.*?)\]:\s*(\S+)(?:\s+(?:(['"])(.*?)\3|\((.*?)\)))?\n?/);
+      if (m) {
+
+        var attrs = create_attrs.call(this);
+        create_reference(attrs, m);
+
+        return [ m[0].length ]
+      }
+
       // [id]
       // Only if id is plain (no formatting.)
       if ( children.length == 1 && typeof children[0] == "string" ) {
diff --git a/test/javascripts/components/markdown_test.js b/test/javascripts/components/markdown_test.js
index 3450e457e4c..b4a22bf1b07 100644
--- a/test/javascripts/components/markdown_test.js
+++ b/test/javascripts/components/markdown_test.js
@@ -103,6 +103,10 @@ test("Links", function() {
          "<a href=\"http://www.imdb.com/name/nm2225369\">http://www.imdb.com/name/nm2225369</a></p>",
          'allows multiple links on one line');
 
+  cooked("* [Evil Trout][1]\n  [1]: http://eviltrout.com",
+         "<ul><li><a href=\"http://eviltrout.com\">Evil Trout</a><br></li></ul>",
+         "allows markdown link references in a list");
+
 });
 
 test("Quotes", function() {