浏览代码

Merge branch 'develop' into feature/event_mediator

Conflicts:
	dist/showdown.js
	dist/showdown.js.map
	dist/showdown.min.js
	dist/showdown.min.js.map
	src/converter.js
	src/subParsers/blockGamut.js
	src/subParsers/codeSpans.js
Estevão Soares dos Santos 9 年之前
父节点
当前提交
f4cb29e05a
共有 68 个文件被更改,包括 462 次插入69 次删除
  1. 12 0
      CHANGELOG.md
  2. 33 6
      Gruntfile.js
  3. 10 0
      README.md
  4. 118 24
      dist/showdown.js
  5. 0 0
      dist/showdown.js.map
  6. 1 1
      dist/showdown.min.js
  7. 0 0
      dist/showdown.min.js.map
  8. 4 2
      package.json
  9. 10 8
      src/converter.js
  10. 66 0
      src/helpers.js
  11. 2 2
      src/subParsers/autoLinks.js
  12. 4 2
      src/subParsers/blockGamut.js
  13. 2 1
      src/subParsers/blockQuotes.js
  14. 0 7
      src/subParsers/codeSpans.js
  15. 26 0
      src/subParsers/hashHTMLSpans.js
  16. 2 2
      src/subParsers/italicsAndBold.js
  17. 2 0
      src/subParsers/lists.js
  18. 9 0
      test/cases/blockquote-followed-by-code.html
  19. 8 0
      test/cases/blockquote-followed-by-code.md
  20. 8 0
      test/cases/blockquote-inside-code.html
  21. 7 0
      test/cases/blockquote-inside-code.md
  22. 15 0
      test/cases/github-style-codeblock-inside-quote.html
  23. 13 0
      test/cases/github-style-codeblock-inside-quote.md
  24. 9 0
      test/cases/literal-html-tags.html
  25. 9 0
      test/cases/literal-html-tags.md
  26. 0 0
      test/features/#143.support-image-dimensions.html
  27. 0 0
      test/features/#143.support-image-dimensions.md
  28. 0 0
      test/features/#164.1.simple-autolink.html
  29. 0 0
      test/features/#164.1.simple-autolink.md
  30. 0 0
      test/features/#164.2.disallow-underscore-emphasis-mid-word.html
  31. 0 0
      test/features/#164.2.disallow-underscore-emphasis-mid-word.md
  32. 5 0
      test/features/#198.literalMidWordUnderscores-changes-behavior-of-asterisk.html
  33. 5 0
      test/features/#198.literalMidWordUnderscores-changes-behavior-of-asterisk.md
  34. 7 0
      test/features/#204.certain-links-with-at-and-dot-break-url.html
  35. 7 0
      test/features/#204.certain-links-with-at-and-dot-break-url.md
  36. 0 0
      test/features/#69.header-level-start.html
  37. 0 0
      test/features/#69.header-level-start.md
  38. 0 0
      test/features/autolink-and-disallow-underscores.html
  39. 0 0
      test/features/autolink-and-disallow-underscores.md
  40. 0 0
      test/features/disable-gh-codeblocks.html
  41. 0 0
      test/features/disable-gh-codeblocks.md
  42. 0 0
      test/features/tables/#179.parse-md-in-table-ths.html
  43. 0 0
      test/features/tables/#179.parse-md-in-table-ths.md
  44. 0 0
      test/features/tables/basic-with-header-ids.html
  45. 0 0
      test/features/tables/basic-with-header-ids.md
  46. 8 0
      test/features/tables/table-inside-codeblock.html
  47. 8 0
      test/features/tables/table-inside-codeblock.md
  48. 0 0
      test/issues/#107.inner-underscore-parse-to-block.html
  49. 0 0
      test/issues/#107.inner-underscore-parse-to-block.md
  50. 0 0
      test/issues/#142.odd-behaviour-for-multiple-consecutive-lists.html
  51. 0 0
      test/issues/#142.odd-behaviour-for-multiple-consecutive-lists.md
  52. 0 0
      test/issues/#150.hyphens-are-getting-removed.html
  53. 0 0
      test/issues/#150.hyphens-are-getting-removed.md
  54. 0 0
      test/issues/#183.gh-code-blocks-within-lists-do-not-render-properly.html
  55. 0 0
      test/issues/#183.gh-code-blocks-within-lists-do-not-render-properly.md
  56. 4 0
      test/issues/#191.blockquote-followed-by-an-heading.html
  57. 2 0
      test/issues/#191.blockquote-followed-by-an-heading.md
  58. 20 0
      test/issues/#196.entity-in-code-block-in-nested-list.html
  59. 10 0
      test/issues/#196.entity-in-code-block-in-nested-list.md
  60. 0 0
      test/issues/#83.parsed-text-links-with-underscores.html
  61. 0 0
      test/issues/#83.parsed-text-links-with-underscores.md
  62. 0 0
      test/issues/#96.underscores-in-links.html
  63. 0 0
      test/issues/#96.underscores-in-links.md
  64. 12 10
      test/node/testsuite.features.js
  65. 1 1
      test/node/testsuite.ghost.js
  66. 1 1
      test/node/testsuite.issues.js
  67. 1 1
      test/node/testsuite.karlcow.js
  68. 1 1
      test/node/testsuite.standard.js

+ 12 - 0
CHANGELOG.md

@@ -1,3 +1,15 @@
+<a name"1.2.3"></a>
+### 1.2.3 (2015-08-27)
+
+
+#### Bug Fixes
+
+* **blockGamut:** fix for headings inside blockquotes ([3df70624](http://github.com/showdownjs/showdown/commit/3df70624), closes [#191](http://github.com/showdownjs/showdown/issues/191))
+* **blockQuote:** fix 'github style codeblocks' not being parsed inside 'blockquote' ([ed2cf595](http://github.com/showdownjs/showdown/commit/ed2cf595), closes [#192](http://github.com/showdownjs/showdown/issues/192))
+* **simpleAutoLinks:** fix emails being treated as simple urls ([7dc3fb1d](http://github.com/showdownjs/showdown/commit/7dc3fb1d), closes [#187](http://github.com/showdownjs/showdown/issues/187))
+* **tables:** fix md tables being parsed inside indented code blocks. ([50256233](http://github.com/showdownjs/showdown/commit/50256233), closes [#193](http://github.com/showdownjs/showdown/issues/193))
+
+
 <a name"1.2.2"></a>
 ### 1.2.2 (2015-08-02)
 

+ 33 - 6
Gruntfile.js

@@ -16,7 +16,7 @@ module.exports = function (grunt) {
       options: {
         sourceMap: true,
         banner: ';/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n(function(){\n',
-        footer: '}).call(this);'
+        footer: '}).call(this);\n'
       },
       dist: {
         src: [
@@ -52,6 +52,15 @@ module.exports = function (grunt) {
       }
     },
 
+    endline: {
+      dist: {
+        files: {
+          'dist/<%= pkg.name %>.js': 'dist/<%= pkg.name %>.js',
+          'dist/<%= pkg.name %>.min.js': 'dist/<%= pkg.name %>.min.js'
+        }
+      }
+    },
+
     jshint: {
       options: {
         jshintrc: '.jshintrc'
@@ -76,10 +85,28 @@ module.exports = function (grunt) {
       }
     },
 
-    changelog: {
+    conventionalChangelog: {
       options: {
-        repository: 'http://github.com/showdownjs/showdown',
-        dest: 'CHANGELOG.md'
+        changelogOpts: {
+          preset: 'angular'
+        }
+      },
+      release: {
+        src: 'CHANGELOG.md'
+      }
+    },
+
+    conventionalGithubReleaser: {
+      release: {
+        options: {
+          auth: {
+            type: 'oauth',
+            token: process.env.GH_TOEKN
+          },
+          changelogOpts: {
+            preset: 'angular'
+          }
+        }
       }
     },
 
@@ -162,8 +189,8 @@ module.exports = function (grunt) {
 
   grunt.registerTask('lint', ['jshint', 'jscs']);
   grunt.registerTask('test', ['clean', 'lint', 'concat:test', 'simplemocha:node', 'clean']);
-  grunt.registerTask('build', ['test', 'concat:dist', 'uglify']);
-  grunt.registerTask('prep-release', ['build', 'changelog']);
+  grunt.registerTask('build', ['test', 'concat:dist', 'uglify', 'endline']);
+  grunt.registerTask('prep-release', ['build', 'conventionalChangelog']);
 
   // Default task(s).
   grunt.registerTask('default', ['test']);

+ 10 - 0
README.md

@@ -11,6 +11,16 @@ Showdown is a Javascript Markdown to HTML converter, based on the original works
 Check a live Demo here http://showdownjs.github.io/demo/
 
 
+## Who uses Showdown (or a fork)
+
+ - [GoogleCloudPlatform](https://github.com/GoogleCloudPlatform)
+ - [Ghost](https://ghost.org/)
+ - [Meteor](https://www.meteor.com/)
+ - [Stackexchange](http://stackexchange.com/) - forked as [PageDown](https://code.google.com/p/pagedown/)
+ - [docular](https://github.com/Vertafore/docular)
+ - [and some others...](https://www.npmjs.com/browse/depended/showdown)
+
+
 ## Installation
 
 ### Download tarball

+ 118 - 24
dist/showdown.js

@@ -1,4 +1,4 @@
-;/*! showdown 03-08-2015 */
+;/*! showdown 19-10-2015 */
 (function(){
 /**
  * Created by Tivie on 13-07-2015.
@@ -520,6 +520,68 @@ showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape
   return text;
 };
 
+/**
+ * matchRecursiveRegExp
+ *
+ * (c) 2007 Steven Levithan <stevenlevithan.com>
+ * MIT License
+ *
+ * Accepts a string to search, a left and right format delimiter
+ * as regex patterns, and optional regex flags. Returns an array
+ * of matches, allowing nested instances of left/right delimiters.
+ * Use the "g" flag to return all matches, otherwise only the
+ * first is returned. Be careful to ensure that the left and
+ * right format delimiters produce mutually exclusive matches.
+ * Backreferences are not supported within the right delimiter
+ * due to how it is internally combined with the left delimiter.
+ * When matching strings whose format delimiters are unbalanced
+ * to the left or right, the output is intentionally as a
+ * conventional regex library with recursion support would
+ * produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
+ * "<" and ">" as the delimiters (both strings contain a single,
+ * balanced instance of "<x>").
+ *
+ * examples:
+ * matchRecursiveRegExp("test", "\\(", "\\)")
+ * returns: []
+ * matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
+ * returns: ["t<<e>><s>", ""]
+ * matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
+ * returns: ["test"]
+ */
+showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) {
+  'use strict';
+  var	f = flags || '',
+    g = f.indexOf('g') > -1,
+    x = new RegExp(left + '|' + right, f),
+    l = new RegExp(left, f.replace(/g/g, '')),
+    a = [],
+    t, s, m, start, end;
+
+  do {
+    t = 0;
+    while ((m = x.exec(str))) {
+      if (l.test(m[0])) {
+        if (!(t++)) {
+          start = m[0];
+          s = x.lastIndex;
+        }
+      } else if (t) {
+        if (!--t) {
+          end = m[0];
+          var match = str.slice(s, m.index);
+          a.push([start + match + end, match]);
+          if (!g) {
+            return a;
+          }
+        }
+      }
+    }
+  } while (t && (x.lastIndex = s));
+
+  return a;
+};
+
 /**
  * POLYFILLS
  */
@@ -532,6 +594,10 @@ if (showdown.helper.isUndefined(console)) {
     log: function (msg) {
       'use strict';
       alert(msg);
+    },
+    error: function (msg) {
+      'use strict';
+      throw msg;
     }
   };
 }
@@ -544,12 +610,7 @@ if (showdown.helper.isUndefined(console)) {
  * Showdown Converter class
  * @class
  * @param {object} [converterOptions]
- * @returns {
- *  {makeHtml: Function},
- *  {setOption: Function},
- *  {getOption: Function},
- *  {getOptions: Function}
- * }
+ * @returns {Converter}
  */
 showdown.Converter = function (converterOptions) {
   'use strict';
@@ -576,8 +637,12 @@ showdown.Converter = function (converterOptions) {
        */
       outputModifiers = [],
 
-      listeners = {
-      };
+      /**
+       * Event listeners
+       * @private
+       * @type {{}}
+       */
+      listeners = {};
 
   _constructor();
 
@@ -772,6 +837,7 @@ showdown.Converter = function (converterOptions) {
 
     var globals = {
       gHtmlBlocks:     [],
+      gHtmlSpans:      [],
       gUrls:           {},
       gTitles:         {},
       gDimensions:     {},
@@ -814,8 +880,10 @@ showdown.Converter = function (converterOptions) {
     // run the sub parsers
     text = showdown.subParser('githubCodeBlocks')(text, options, globals);
     text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
+    text = showdown.subParser('hashHTMLSpans')(text, options, globals);
     text = showdown.subParser('stripLinkDefinitions')(text, options, globals);
     text = showdown.subParser('blockGamut')(text, options, globals);
+    text = showdown.subParser('unhashHTMLSpans')(text, options, globals);
     text = showdown.subParser('unescapeSpecialChars')(text, options, globals);
 
     // attacklab: Restore dollar signs
@@ -1069,7 +1137,7 @@ showdown.subParser('autoLinks', function (text, options, globals) {
 
   var simpleURLRegex  = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi,
       delimUrlRegex   = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,
-      simpleMailRegex = /\b(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)\b/gi,
+      simpleMailRegex = /(?:^|[ \n\t])([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|[ \n\t])/gi,
       delimMailRegex  = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
 
   text = text.replace(delimUrlRegex, '<a href=\"$1\">$1</a>');
@@ -1079,7 +1147,7 @@ showdown.subParser('autoLinks', function (text, options, globals) {
 
   if (options.simplifiedAutoLink) {
     text = text.replace(simpleURLRegex, '<a href=\"$1\">$1</a>');
-    text = text.replace(simpleMailRegex, '<a href=\"$1\">$1</a>');
+    text = text.replace(simpleMailRegex, replaceMail);
   }
 
   function replaceMail(wholeMatch, m1) {
@@ -1101,6 +1169,9 @@ showdown.subParser('blockGamut', function (text, options, globals) {
 
   text = globals.converter._dispatch('blockGamut.before', text, options);
 
+  // we parse blockquotes first so that we can have headings and hrs
+  // inside blockquotes
+  text = showdown.subParser('blockQuotes')(text, options, globals);
   text = showdown.subParser('headers')(text, options, globals);
 
   // Do Horizontal Rules:
@@ -1109,10 +1180,9 @@ showdown.subParser('blockGamut', function (text, options, globals) {
   text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key);
   text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key);
 
-  text = showdown.subParser('tables')(text, options, globals);
   text = showdown.subParser('lists')(text, options, globals);
   text = showdown.subParser('codeBlocks')(text, options, globals);
-  text = showdown.subParser('blockQuotes')(text, options, globals);
+  text = showdown.subParser('tables')(text, options, globals);
 
   // We already ran _HashHTMLBlocks() before, in Markdown(), but that
   // was to escape raw HTML in the original Markdown source. This time,
@@ -1143,7 +1213,7 @@ showdown.subParser('blockQuotes', function (text, options, globals) {
    /gm, function(){...});
    */
 
-  text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) {
+  text = text.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) {
     var bq = m1;
 
     // attacklab: hack around Konqueror 3.5.4 bug:
@@ -1154,6 +1224,7 @@ showdown.subParser('blockQuotes', function (text, options, globals) {
     bq = bq.replace(/~0/g, '');
 
     bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
+    bq = showdown.subParser('githubCodeBlocks')(bq, options, globals);
     bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
 
     bq = bq.replace(/(^|\n)/g, '$1  ');
@@ -1253,13 +1324,6 @@ showdown.subParser('codeSpans', function (text, options, globals) {
   'use strict';
 
   text = globals.converter._dispatch('codeSpans.before', text, options);
-  //special case -> literal html code tag
-  text = text.replace(/(<code[^><]*?>)([^]*?)<\/code>/g, function (wholeMatch, tag, c) {
-    c = c.replace(/^([ \t]*)/g, '');	// leading whitespace
-    c = c.replace(/[ \t]*$/g, '');	// trailing whitespace
-    c = showdown.subParser('encodeCode')(c);
-    return tag + c + '</code>';
-  });
 
   /*
    text = text.replace(/
@@ -1658,6 +1722,33 @@ showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
 
 });
 
+/**
+ * Hash span elements that should not be parsed as markdown
+ */
+showdown.subParser('hashHTMLSpans', function (text, config, globals) {
+  'use strict';
+
+  var matches = showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');
+
+  for (var i = 0; i < matches.length; ++i) {
+    text = text.replace(matches[i][0], '~L' + (globals.gHtmlSpans.push(matches[i][0]) - 1) + 'L');
+  }
+  return text;
+});
+
+/**
+ * Unhash HTML spans
+ */
+showdown.subParser('unhashHTMLSpans', function (text, config, globals) {
+  'use strict';
+
+  for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
+    text = text.replace('~L' + i + 'L', globals.gHtmlSpans[i]);
+  }
+
+  return text;
+});
+
 showdown.subParser('headers', function (text, options, globals) {
   'use strict';
 
@@ -1823,8 +1914,8 @@ showdown.subParser('italicsAndBold', function (text, options, globals) {
     text = text.replace(/(^|\s|>|\b)__(?=\S)([^]+?)__(?=\b|<|\s|$)/gm, '$1<strong>$2</strong>');
     text = text.replace(/(^|\s|>|\b)_(?=\S)([^]+?)_(?=\b|<|\s|$)/gm, '$1<em>$2</em>');
     //asterisks
-    text = text.replace(/\*\*(?=\S)([^]+?)\*\*/g, '<strong>$1</strong>');
-    text = text.replace(/\*(?=\S)([^]+?)\*/g, '<em>$1</em>');
+    text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '<strong>$2</strong>');
+    text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
 
   } else {
     // <strong> must go first:
@@ -1847,6 +1938,7 @@ showdown.subParser('lists', function (text, options, globals) {
    * Process the contents of a single ordered or unordered list, splitting it
    * into individual list items.
    * @param {string} listStr
+   * @param {boolean} trimTrailing
    * @returns {string}
    */
   function processListItems (listStr, trimTrailing) {
@@ -1934,6 +2026,7 @@ showdown.subParser('lists', function (text, options, globals) {
    * Check and parse consecutive lists (better fix for issue #142)
    * @param {string} list
    * @param {string} listType
+   * @param {boolean} trimTrailing
    * @returns {string}
    */
   function parseConsecutiveLists(list, listType, trimTrailing) {
@@ -2394,4 +2487,5 @@ if (typeof module !== 'undefined' && module.exports) {
   root.showdown = showdown;
 }
 }).call(this);
-//# sourceMappingURL=showdown.js.map
+
+//# sourceMappingURL=showdown.js.map

文件差异内容过多而无法显示
+ 0 - 0
dist/showdown.js.map


文件差异内容过多而无法显示
+ 1 - 1
dist/showdown.min.js


文件差异内容过多而无法显示
+ 0 - 0
dist/showdown.min.js.map


+ 4 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "showdown",
-  "version": "1.2.2",
+  "version": "1.2.3",
   "description": "A Markdown to HTML converter written in Javascript",
   "author": "Estevão Santos",
   "homepage": "http://showdownjs.github.io/showdown/",
@@ -45,7 +45,9 @@
     "grunt-contrib-concat": "^0.5.0",
     "grunt-contrib-jshint": "^0.10.0",
     "grunt-contrib-uglify": "^0.6.0",
-    "grunt-conventional-changelog": "^1.1.0",
+    "grunt-conventional-changelog": "^4.0.0",
+    "grunt-conventional-github-releaser": "^0.3.0",
+    "grunt-endline": "^0.4.0",
     "grunt-jscs": "^1.2.0",
     "grunt-simple-mocha": "^0.4.0",
     "js-beautify": "^1.5.6",

+ 10 - 8
src/converter.js

@@ -6,12 +6,7 @@
  * Showdown Converter class
  * @class
  * @param {object} [converterOptions]
- * @returns {
- *  {makeHtml: Function},
- *  {setOption: Function},
- *  {getOption: Function},
- *  {getOptions: Function}
- * }
+ * @returns {Converter}
  */
 showdown.Converter = function (converterOptions) {
   'use strict';
@@ -38,8 +33,12 @@ showdown.Converter = function (converterOptions) {
        */
       outputModifiers = [],
 
-      listeners = {
-      };
+      /**
+       * Event listeners
+       * @private
+       * @type {{}}
+       */
+      listeners = {};
 
   _constructor();
 
@@ -234,6 +233,7 @@ showdown.Converter = function (converterOptions) {
 
     var globals = {
       gHtmlBlocks:     [],
+      gHtmlSpans:      [],
       gUrls:           {},
       gTitles:         {},
       gDimensions:     {},
@@ -276,8 +276,10 @@ showdown.Converter = function (converterOptions) {
     // run the sub parsers
     text = showdown.subParser('githubCodeBlocks')(text, options, globals);
     text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
+    text = showdown.subParser('hashHTMLSpans')(text, options, globals);
     text = showdown.subParser('stripLinkDefinitions')(text, options, globals);
     text = showdown.subParser('blockGamut')(text, options, globals);
+    text = showdown.subParser('unhashHTMLSpans')(text, options, globals);
     text = showdown.subParser('unescapeSpecialChars')(text, options, globals);
 
     // attacklab: Restore dollar signs

+ 66 - 0
src/helpers.js

@@ -106,6 +106,68 @@ showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape
   return text;
 };
 
+/**
+ * matchRecursiveRegExp
+ *
+ * (c) 2007 Steven Levithan <stevenlevithan.com>
+ * MIT License
+ *
+ * Accepts a string to search, a left and right format delimiter
+ * as regex patterns, and optional regex flags. Returns an array
+ * of matches, allowing nested instances of left/right delimiters.
+ * Use the "g" flag to return all matches, otherwise only the
+ * first is returned. Be careful to ensure that the left and
+ * right format delimiters produce mutually exclusive matches.
+ * Backreferences are not supported within the right delimiter
+ * due to how it is internally combined with the left delimiter.
+ * When matching strings whose format delimiters are unbalanced
+ * to the left or right, the output is intentionally as a
+ * conventional regex library with recursion support would
+ * produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
+ * "<" and ">" as the delimiters (both strings contain a single,
+ * balanced instance of "<x>").
+ *
+ * examples:
+ * matchRecursiveRegExp("test", "\\(", "\\)")
+ * returns: []
+ * matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
+ * returns: ["t<<e>><s>", ""]
+ * matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
+ * returns: ["test"]
+ */
+showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) {
+  'use strict';
+  var	f = flags || '',
+    g = f.indexOf('g') > -1,
+    x = new RegExp(left + '|' + right, f),
+    l = new RegExp(left, f.replace(/g/g, '')),
+    a = [],
+    t, s, m, start, end;
+
+  do {
+    t = 0;
+    while ((m = x.exec(str))) {
+      if (l.test(m[0])) {
+        if (!(t++)) {
+          start = m[0];
+          s = x.lastIndex;
+        }
+      } else if (t) {
+        if (!--t) {
+          end = m[0];
+          var match = str.slice(s, m.index);
+          a.push([start + match + end, match]);
+          if (!g) {
+            return a;
+          }
+        }
+      }
+    }
+  } while (t && (x.lastIndex = s));
+
+  return a;
+};
+
 /**
  * POLYFILLS
  */
@@ -118,6 +180,10 @@ if (showdown.helper.isUndefined(console)) {
     log: function (msg) {
       'use strict';
       alert(msg);
+    },
+    error: function (msg) {
+      'use strict';
+      throw msg;
     }
   };
 }

+ 2 - 2
src/subParsers/autoLinks.js

@@ -5,7 +5,7 @@ showdown.subParser('autoLinks', function (text, options, globals) {
 
   var simpleURLRegex  = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi,
       delimUrlRegex   = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,
-      simpleMailRegex = /\b(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)\b/gi,
+      simpleMailRegex = /(?:^|[ \n\t])([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|[ \n\t])/gi,
       delimMailRegex  = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
 
   text = text.replace(delimUrlRegex, '<a href=\"$1\">$1</a>');
@@ -15,7 +15,7 @@ showdown.subParser('autoLinks', function (text, options, globals) {
 
   if (options.simplifiedAutoLink) {
     text = text.replace(simpleURLRegex, '<a href=\"$1\">$1</a>');
-    text = text.replace(simpleMailRegex, '<a href=\"$1\">$1</a>');
+    text = text.replace(simpleMailRegex, replaceMail);
   }
 
   function replaceMail(wholeMatch, m1) {

+ 4 - 2
src/subParsers/blockGamut.js

@@ -7,6 +7,9 @@ showdown.subParser('blockGamut', function (text, options, globals) {
 
   text = globals.converter._dispatch('blockGamut.before', text, options);
 
+  // we parse blockquotes first so that we can have headings and hrs
+  // inside blockquotes
+  text = showdown.subParser('blockQuotes')(text, options, globals);
   text = showdown.subParser('headers')(text, options, globals);
 
   // Do Horizontal Rules:
@@ -15,10 +18,9 @@ showdown.subParser('blockGamut', function (text, options, globals) {
   text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key);
   text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key);
 
-  text = showdown.subParser('tables')(text, options, globals);
   text = showdown.subParser('lists')(text, options, globals);
   text = showdown.subParser('codeBlocks')(text, options, globals);
-  text = showdown.subParser('blockQuotes')(text, options, globals);
+  text = showdown.subParser('tables')(text, options, globals);
 
   // We already ran _HashHTMLBlocks() before, in Markdown(), but that
   // was to escape raw HTML in the original Markdown source. This time,

+ 2 - 1
src/subParsers/blockQuotes.js

@@ -15,7 +15,7 @@ showdown.subParser('blockQuotes', function (text, options, globals) {
    /gm, function(){...});
    */
 
-  text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) {
+  text = text.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) {
     var bq = m1;
 
     // attacklab: hack around Konqueror 3.5.4 bug:
@@ -26,6 +26,7 @@ showdown.subParser('blockQuotes', function (text, options, globals) {
     bq = bq.replace(/~0/g, '');
 
     bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
+    bq = showdown.subParser('githubCodeBlocks')(bq, options, globals);
     bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
 
     bq = bq.replace(/(^|\n)/g, '$1  ');

+ 0 - 7
src/subParsers/codeSpans.js

@@ -27,13 +27,6 @@ showdown.subParser('codeSpans', function (text, options, globals) {
   'use strict';
 
   text = globals.converter._dispatch('codeSpans.before', text, options);
-  //special case -> literal html code tag
-  text = text.replace(/(<code[^><]*?>)([^]*?)<\/code>/g, function (wholeMatch, tag, c) {
-    c = c.replace(/^([ \t]*)/g, '');	// leading whitespace
-    c = c.replace(/[ \t]*$/g, '');	// trailing whitespace
-    c = showdown.subParser('encodeCode')(c);
-    return tag + c + '</code>';
-  });
 
   /*
    text = text.replace(/

+ 26 - 0
src/subParsers/hashHTMLSpans.js

@@ -0,0 +1,26 @@
+/**
+ * Hash span elements that should not be parsed as markdown
+ */
+showdown.subParser('hashHTMLSpans', function (text, config, globals) {
+  'use strict';
+
+  var matches = showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');
+
+  for (var i = 0; i < matches.length; ++i) {
+    text = text.replace(matches[i][0], '~L' + (globals.gHtmlSpans.push(matches[i][0]) - 1) + 'L');
+  }
+  return text;
+});
+
+/**
+ * Unhash HTML spans
+ */
+showdown.subParser('unhashHTMLSpans', function (text, config, globals) {
+  'use strict';
+
+  for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
+    text = text.replace('~L' + i + 'L', globals.gHtmlSpans[i]);
+  }
+
+  return text;
+});

+ 2 - 2
src/subParsers/italicsAndBold.js

@@ -9,8 +9,8 @@ showdown.subParser('italicsAndBold', function (text, options, globals) {
     text = text.replace(/(^|\s|>|\b)__(?=\S)([^]+?)__(?=\b|<|\s|$)/gm, '$1<strong>$2</strong>');
     text = text.replace(/(^|\s|>|\b)_(?=\S)([^]+?)_(?=\b|<|\s|$)/gm, '$1<em>$2</em>');
     //asterisks
-    text = text.replace(/\*\*(?=\S)([^]+?)\*\*/g, '<strong>$1</strong>');
-    text = text.replace(/\*(?=\S)([^]+?)\*/g, '<em>$1</em>');
+    text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '<strong>$2</strong>');
+    text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
 
   } else {
     // <strong> must go first:

+ 2 - 0
src/subParsers/lists.js

@@ -9,6 +9,7 @@ showdown.subParser('lists', function (text, options, globals) {
    * Process the contents of a single ordered or unordered list, splitting it
    * into individual list items.
    * @param {string} listStr
+   * @param {boolean} trimTrailing
    * @returns {string}
    */
   function processListItems (listStr, trimTrailing) {
@@ -96,6 +97,7 @@ showdown.subParser('lists', function (text, options, globals) {
    * Check and parse consecutive lists (better fix for issue #142)
    * @param {string} list
    * @param {string} listType
+   * @param {boolean} trimTrailing
    * @returns {string}
    */
   function parseConsecutiveLists(list, listType, trimTrailing) {

+ 9 - 0
test/cases/blockquote-followed-by-code.html

@@ -0,0 +1,9 @@
+<blockquote>
+    <p>a blockquote with a 4 space indented line (not code)</p>
+</blockquote>
+<p>sep</p>
+<blockquote>
+    <p>a blockquote</p>
+</blockquote>
+<pre><code>with some code after
+</code></pre>

+ 8 - 0
test/cases/blockquote-followed-by-code.md

@@ -0,0 +1,8 @@
+> a blockquote
+    with a 4 space indented line (not code)
+
+sep
+
+> a blockquote
+
+    with some code after

+ 8 - 0
test/cases/blockquote-inside-code.html

@@ -0,0 +1,8 @@
+<pre><code>&gt; this is a pseudo blockquote
+    &gt; inside a code block
+</code></pre>
+
+<p>foo</p>
+<pre><code>&gt; this is another bq
+    inside code
+</code></pre>

+ 7 - 0
test/cases/blockquote-inside-code.md

@@ -0,0 +1,7 @@
+    > this is a pseudo blockquote
+    > inside a code block
+
+foo
+
+    > this is another bq
+    inside code

+ 15 - 0
test/cases/github-style-codeblock-inside-quote.html

@@ -0,0 +1,15 @@
+<blockquote>
+<p>Define a function in javascript:</p>
+
+<pre><code>function MyFunc(a) {
+  var s = '`';
+  }
+</code></pre>
+
+<blockquote>
+<p>And some nested quote</p>
+
+<pre><code class="html language-html">&lt;div&gt;HTML!&lt;/div&gt;
+</code></pre>
+</blockquote>
+</blockquote>

+ 13 - 0
test/cases/github-style-codeblock-inside-quote.md

@@ -0,0 +1,13 @@
+> Define a function in javascript:
+>
+> ```
+> function MyFunc(a) {
+>     var s = '`';
+> }
+> ```
+>
+>> And some nested quote
+>>
+>> ```html
+>> <div>HTML!</div>
+>> ```

+ 9 - 0
test/cases/literal-html-tags.html

@@ -0,0 +1,9 @@
+<p><code>some **code** yeah</code></p>
+
+<p>some <code>inline **code** block</code></p>
+
+<p><code>some inline **code**</code> block</p>
+
+<p>yo dawg <code start="true">some <code start="false">code</code> inception</code></p>
+
+<div>some **div** yeah</div>

+ 9 - 0
test/cases/literal-html-tags.md

@@ -0,0 +1,9 @@
+<code>some **code** yeah</code>
+
+some <code>inline **code** block</code>
+
+<code>some inline **code**</code> block
+
+yo dawg <code start="true">some <code start="false">code</code> inception</code>
+
+<div>some **div** yeah</div>

+ 0 - 0
test/features/#143.support_image_dimensions.html → test/features/#143.support-image-dimensions.html


+ 0 - 0
test/features/#143.support_image_dimensions.md → test/features/#143.support-image-dimensions.md


+ 0 - 0
test/features/#164.1.simple_autolink.html → test/features/#164.1.simple-autolink.html


+ 0 - 0
test/features/#164.1.simple_autolink.md → test/features/#164.1.simple-autolink.md


+ 0 - 0
test/features/#164.2.disallow_underscore_emphasis_mid_word.html → test/features/#164.2.disallow-underscore-emphasis-mid-word.html


+ 0 - 0
test/features/#164.2.disallow_underscore_emphasis_mid_word.md → test/features/#164.2.disallow-underscore-emphasis-mid-word.md


+ 5 - 0
test/features/#198.literalMidWordUnderscores-changes-behavior-of-asterisk.html

@@ -0,0 +1,5 @@
+<p>​pointer *ptr *thing</p>
+
+<p>something _else _bla</p>
+
+<p>something __else __bla</p>

+ 5 - 0
test/features/#198.literalMidWordUnderscores-changes-behavior-of-asterisk.md

@@ -0,0 +1,5 @@
+​pointer *ptr *thing
+
+something _else _bla
+
+something __else __bla

+ 7 - 0
test/features/#204.certain-links-with-at-and-dot-break-url.html

@@ -0,0 +1,7 @@
+<p><a href="http://website.com/img@x2.jpg">http://website.com/img@x2.jpg</a></p>
+
+<p><a href="http://website.com/img-x2.jpg">http://website.com/img-x2.jpg</a></p>
+
+<p><a href="http://website.com/img@x2">http://website.com/img@x2</a></p>
+
+<p><a href="http://website.com/img@.jpg">http://website.com/img@.jpg</a></p>

+ 7 - 0
test/features/#204.certain-links-with-at-and-dot-break-url.md

@@ -0,0 +1,7 @@
+http://website.com/img@x2.jpg
+
+http://website.com/img-x2.jpg
+
+http://website.com/img@x2
+
+http://website.com/img@.jpg

+ 0 - 0
test/features/#69.header_level_start.html → test/features/#69.header-level-start.html


+ 0 - 0
test/features/#69.header_level_start.md → test/features/#69.header-level-start.md


+ 0 - 0
test/features/autolink_and_disallow_underscores.html → test/features/autolink-and-disallow-underscores.html


+ 0 - 0
test/features/autolink_and_disallow_underscores.md → test/features/autolink-and-disallow-underscores.md


+ 0 - 0
test/features/disable_gh_codeblocks.html → test/features/disable-gh-codeblocks.html


+ 0 - 0
test/features/disable_gh_codeblocks.md → test/features/disable-gh-codeblocks.md


+ 0 - 0
test/features/tables/#179.parse_md_in_table_ths.html → test/features/tables/#179.parse-md-in-table-ths.html


+ 0 - 0
test/features/tables/#179.parse_md_in_table_ths.md → test/features/tables/#179.parse-md-in-table-ths.md


+ 0 - 0
test/features/tables/basic_with_header_ids.html → test/features/tables/basic-with-header-ids.html


+ 0 - 0
test/features/tables/basic_with_header_ids.md → test/features/tables/basic-with-header-ids.md


+ 8 - 0
test/features/tables/table-inside-codeblock.html

@@ -0,0 +1,8 @@
+<p>some text</p>
+
+<pre><code>| Tables        | Are           | Cool  |
+| ------------- |:-------------:| -----:|
+| **col 3 is**  | right-aligned | $1600 |
+| col 2 is      | *centered*    |   $12 |
+| zebra stripes | ~~are neat~~  |    $1 |
+</code></pre>

+ 8 - 0
test/features/tables/table-inside-codeblock.md

@@ -0,0 +1,8 @@
+some text
+
+
+    | Tables        | Are           | Cool  |
+    | ------------- |:-------------:| -----:|
+    | **col 3 is**  | right-aligned | $1600 |
+    | col 2 is      | *centered*    |   $12 |
+    | zebra stripes | ~~are neat~~  |    $1 |

+ 0 - 0
test/issues/#107.inner_underscore_parse_to_block.html → test/issues/#107.inner-underscore-parse-to-block.html


+ 0 - 0
test/issues/#107.inner_underscore_parse_to_block.md → test/issues/#107.inner-underscore-parse-to-block.md


+ 0 - 0
test/issues/#142.odd_behaviour_for_multiple_consecutive_lists.html → test/issues/#142.odd-behaviour-for-multiple-consecutive-lists.html


+ 0 - 0
test/issues/#142.odd_behaviour_for_multiple_consecutive_lists.md → test/issues/#142.odd-behaviour-for-multiple-consecutive-lists.md


+ 0 - 0
test/issues/#150.hyphens are getting removed.html → test/issues/#150.hyphens-are-getting-removed.html


+ 0 - 0
test/issues/#150.hyphens are getting removed.md → test/issues/#150.hyphens-are-getting-removed.md


+ 0 - 0
test/issues/#183.gh_code_blocks_within_lists_do_not_render_properly.html → test/issues/#183.gh-code-blocks-within-lists-do-not-render-properly.html


+ 0 - 0
test/issues/#183.gh_code_blocks_within_lists_do_not_render_properly.md → test/issues/#183.gh-code-blocks-within-lists-do-not-render-properly.md


+ 4 - 0
test/issues/#191.blockquote-followed-by-an-heading.html

@@ -0,0 +1,4 @@
+<blockquote>
+    <p>a blockquote</p>
+    <h1 id="followedbyanheading">followed by an heading</h1>
+</blockquote>

+ 2 - 0
test/issues/#191.blockquote-followed-by-an-heading.md

@@ -0,0 +1,2 @@
+> a blockquote
+# followed by an heading

+ 20 - 0
test/issues/#196.entity-in-code-block-in-nested-list.html

@@ -0,0 +1,20 @@
+<p>Test pre in a list</p>
+<ul>
+    <li>&amp; &lt;</li>
+    <li><code>&amp; &lt;</code>
+        <ul>
+            <li>&amp; &lt;</li>
+            <li><code>&amp; &lt;</code>
+                <ul>
+                    <li>&amp; &lt;</li>
+                    <li><code>&amp; &lt;</code>
+                        <ul>
+                            <li>&amp; &lt;</li>
+                            <li><code>&amp; &lt;</code></li>
+                        </ul>
+                    </li>
+                </ul>
+            </li>
+        </ul>
+    </li>
+</ul>

+ 10 - 0
test/issues/#196.entity-in-code-block-in-nested-list.md

@@ -0,0 +1,10 @@
+Test pre in a list
+
+- & <
+- `& <` 
+   - & <
+   - `& <`
+      - & <
+      - `& <`
+         - & <
+         - `& <`

+ 0 - 0
test/issues/#83.parsed_text_links_with_underscores.html → test/issues/#83.parsed-text-links-with-underscores.html


+ 0 - 0
test/issues/#83.parsed_text_links_with_underscores.md → test/issues/#83.parsed-text-links-with-underscores.md


+ 0 - 0
test/issues/#96.Underscores_in_links.html → test/issues/#96.underscores-in-links.html


+ 0 - 0
test/issues/#96.Underscores_in_links.md → test/issues/#96.underscores-in-links.md


+ 12 - 10
test/node/testsuite.features.js

@@ -11,39 +11,41 @@ describe('makeHtml() features testsuite', function () {
   'use strict';
   for (var i = 0; i < testsuite.length; ++i) {
     var converter;
-    if (testsuite[i].name === '#143.support_image_dimensions') {
+    if (testsuite[i].name === '#143.support-image-dimensions') {
       converter = new showdown.Converter({parseImgDimensions: true});
-    } else if (testsuite[i].name === '#69.header_level_start') {
+    } else if (testsuite[i].name === '#69.header-level-start') {
       converter = new showdown.Converter({headerLevelStart: 3});
-    } else if (testsuite[i].name === '#164.1.simple_autolink') {
+    } else if (testsuite[i].name === '#164.1.simple-autolink' || testsuite[i].name === '#204.certain-links-with-at-and-dot-break-url') {
       converter = new showdown.Converter({simplifiedAutoLink: true});
-    } else if (testsuite[i].name === '#164.2.disallow_underscore_emphasis_mid_word') {
+    } else if (testsuite[i].name === '#164.2.disallow-underscore-emphasis-mid-word') {
       converter = new showdown.Converter({literalMidWordUnderscores: true});
     } else if (testsuite[i].name === '#164.3.strikethrough') {
       converter = new showdown.Converter({strikethrough: true});
-    } else if (testsuite[i].name === 'disable_gh_codeblocks') {
+    } else if (testsuite[i].name === 'disable-gh-codeblocks') {
       converter = new showdown.Converter({ghCodeBlocks: false});
     } else if (testsuite[i].name === '#164.4.tasklists') {
       converter = new showdown.Converter({tasklists: true});
-    } else if (testsuite[i].name === 'autolink_and_disallow_underscores') {
+    } else if (testsuite[i].name === 'autolink-and-disallow-underscores') {
       converter = new showdown.Converter({literalMidWordUnderscores: true, simplifiedAutoLink: true});
+    } else if (testsuite[i].name === '#198.literalMidWordUnderscores-changes-behavior-of-asterisk') {
+      converter = new showdown.Converter({literalMidWordUnderscores: true});
     } else {
       converter = new showdown.Converter();
     }
-    it(testsuite[i].name, assertion(testsuite[i], converter));
+    it(testsuite[i].name.replace(/-/g, ' '), assertion(testsuite[i], converter));
   }
 
   describe('table support', function () {
     var converter;
     for (var i = 0; i < tableSuite.length; ++i) {
-      if (tableSuite[i].name === 'basic_with_header_ids') {
+      if (tableSuite[i].name === 'basic-with-header-ids') {
         converter = new showdown.Converter({tables: true, tableHeaderId: true});
-      } else if (tableSuite[i].name === '#179.parse_md_in_table_ths') {
+      } else if (tableSuite[i].name === '#179.parse-md-in-table-ths') {
         converter = new showdown.Converter({tables: true, strikethrough: true});
       } else {
         converter = new showdown.Converter({tables: true});
       }
-      it(tableSuite[i].name, assertion(tableSuite[i], converter));
+      it(tableSuite[i].name.replace(/-/g, ' '), assertion(tableSuite[i], converter));
     }
   });
 

+ 1 - 1
test/node/testsuite.ghost.js

@@ -17,6 +17,6 @@ var bootstrap = require('../bootstrap.js'),
 describe('makeHtml() ghost testsuite', function () {
   'use strict';
   for (var i = 0; i < testsuite.length; ++i) {
-    it(testsuite[i].name, assertion(testsuite[i], converter));
+    it(testsuite[i].name.replace(/-/g, ' '), assertion(testsuite[i], converter));
   }
 });

+ 1 - 1
test/node/testsuite.issues.js

@@ -9,6 +9,6 @@ var bootstrap = require('../bootstrap.js'),
 describe('makeHtml() issues testsuite', function () {
   'use strict';
   for (var i = 0; i < testsuite.length; ++i) {
-    it(testsuite[i].name, assertion(testsuite[i], converter));
+    it(testsuite[i].name.replace(/-/g, ' '), assertion(testsuite[i], converter));
   }
 });

+ 1 - 1
test/node/testsuite.karlcow.js

@@ -7,6 +7,6 @@ var bootstrap = require('../bootstrap.js'),
 describe('makeHtml() karlcow testsuite', function () {
   'use strict';
   for (var i = 0; i < testsuite.length; ++i) {
-    it(testsuite[i].name, assertion(testsuite[i], converter));
+    it(testsuite[i].name.replace(/-/g, ' '), assertion(testsuite[i], converter));
   }
 });

+ 1 - 1
test/node/testsuite.standard.js

@@ -6,6 +6,6 @@ var bootstrap = require('../bootstrap.js'),
 describe('makeHtml() standard testsuite', function () {
   'use strict';
   for (var i = 0; i < testsuite.length; ++i) {
-    it(testsuite[i].name, assertion(testsuite[i], converter));
+    it(testsuite[i].name.replace(/-/g, ' '), assertion(testsuite[i], converter));
   }
 });

部分文件因为文件数量过多而无法显示