diff --git a/README.md b/README.md index 3d6e856..95f2e2d 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,7 @@ var defaultOptions = showdown.getDefaultOptions(); * **noHeaderId**: (boolean) [default false] Disable the automatic generation of header ids. Setting to true overrides **prefixHeaderId** - * **customizedHeaderId**: (boolean) [default false] Use text in curly braces as header id. + * **customizedHeaderId**: (boolean) [default false] Use text in curly braces as header id. (since v1.7.0) Example: ``` ## Sample header {real-id} will use real-id as id @@ -328,7 +328,9 @@ var defaultOptions = showdown.getDefaultOptions(); NOTE: Prior to version 1.6.1, emails would always be obfuscated through dec and hex encoding. - * **openLinksInNewWindow**: (boolean) [default false] Open all links in new windows (by adding the attribute `target="_blank"` to `` tags) + * **openLinksInNewWindow**: (boolean) [default false] Open all links in new windows (by adding the attribute `target="_blank"` to `` tags) (since v1.7.0) + + * **backslashEscapesHTMLTags**: (boolean) [default false] Support for HTML Tag escaping. ex: `\
foo\
` (since v1.7.2) **NOTE**: Please note that until version 1.6.0, all of these options are ***DISABLED*** by default in the cli tool. diff --git a/dist/showdown.js b/dist/showdown.js index fd063a4..75874b3 100644 Binary files a/dist/showdown.js and b/dist/showdown.js differ diff --git a/dist/showdown.js.map b/dist/showdown.js.map index 213e449..507224d 100644 Binary files a/dist/showdown.js.map and b/dist/showdown.js.map differ diff --git a/dist/showdown.min.js b/dist/showdown.min.js index 88e9000..cdcbe50 100644 Binary files a/dist/showdown.min.js and b/dist/showdown.min.js differ diff --git a/dist/showdown.min.js.map b/dist/showdown.min.js.map index 3b8a1ef..276717b 100644 Binary files a/dist/showdown.min.js.map and b/dist/showdown.min.js.map differ diff --git a/src/options.js b/src/options.js index efe8c6a..70c5f7e 100644 --- a/src/options.js +++ b/src/options.js @@ -125,6 +125,11 @@ function getDefaultOpts (simple) { defaultValue: false, description: 'Open all links in new windows', type: 'boolean' + }, + backslashEscapesHTMLTags: { + defaultValue: false, + description: 'Support for HTML Tag escaping. ex: \
foo\
', + type: 'boolean' } }; if (simple === false) { diff --git a/src/subParsers/anchors.js b/src/subParsers/anchors.js index 5779368..49676ea 100644 --- a/src/subParsers/anchors.js +++ b/src/subParsers/anchors.js @@ -82,8 +82,12 @@ showdown.subParser('anchors', function (text, options, globals) { if (!showdown.helper.isString(options.ghMentionsLink)) { throw new Error('ghMentionsLink option must be a string'); } - var lnk = options.ghMentionsLink.replace(/\{u}/g, username); - return st + '
' + mentions + ''; + var lnk = options.ghMentionsLink.replace(/\{u}/g, username), + target = ''; + if (options.openLinksInNewWindow) { + target = ' target="¨E95Eblank"'; + } + return st + '' + mentions + ''; }); } diff --git a/src/subParsers/hashHTMLBlocks.js b/src/subParsers/hashHTMLBlocks.js index 74b4284..b16b103 100644 --- a/src/subParsers/hashHTMLBlocks.js +++ b/src/subParsers/hashHTMLBlocks.js @@ -48,14 +48,26 @@ showdown.subParser('hashHTMLBlocks', function (text, options, globals) { return '\n\n¨K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n'; }; + if (options.backslashEscapesHTMLTags) { + // encode backslash escaped HTML tags + text = text.replace(/\\<(\/?[^>]+?)>/g, function (wm, inside) { + return '<' + inside + '>'; + }); + } + + // hash HTML Blocks for (var i = 0; i < blockTags.length; ++i) { var opTagPos, - rgx1 = new RegExp('^ {0,3}<' + blockTags[i] + '\\b[^>]*>', 'im'), + rgx1 = new RegExp('^ {0,3}(<' + blockTags[i] + '\\b[^>]*>)', 'im'), patLeft = '<' + blockTags[i] + '\\b[^>]*>', patRight = ''; // 1. Look for the first position of the first opening HTML tag in the text while ((opTagPos = showdown.helper.regexIndexOf(text, rgx1)) !== -1) { + + // if the HTML tag is \ escaped, we need to escape it and break + + //2. Split the text in that position var subTexts = showdown.helper.splitAtIndex(text, opTagPos), //3. Match recursively diff --git a/src/subParsers/lists.js b/src/subParsers/lists.js index 8118714..1b2c07d 100644 --- a/src/subParsers/lists.js +++ b/src/subParsers/lists.js @@ -93,16 +93,14 @@ showdown.subParser('lists', function (text, options, globals) { item = showdown.subParser('lists')(item, options, globals); item = item.replace(/\n$/, ''); // chomp(item) item = showdown.subParser('hashHTMLBlocks')(item, options, globals); + // Colapse double linebreaks item = item.replace(/\n\n+/g, '\n\n'); - // replace double linebreaks with a placeholder - item = item.replace(/\n\n/g, '¨B'); if (isParagraphed) { item = showdown.subParser('paragraphs')(item, options, globals); } else { item = showdown.subParser('spanGamut')(item, options, globals); } - item = item.replace(/¨B/g, '\n\n'); } // now we need to remove the marker (¨A) diff --git a/src/subParsers/spanGamut.js b/src/subParsers/spanGamut.js index b829ba4..489907c 100644 --- a/src/subParsers/spanGamut.js +++ b/src/subParsers/spanGamut.js @@ -32,7 +32,10 @@ showdown.subParser('spanGamut', function (text, options, globals) { // Do hard breaks if (options.simpleLineBreaks) { // GFM style hard breaks - text = text.replace(/\n/g, '
\n'); + // only add line breaks if the text does not contain a block (special case for lists) + if (!/\n\n¨K/.test(text)) { + text = text.replace(/\n+/g, '
\n'); + } } else { // Vanilla hard breaks text = text.replace(/ +\n/g, '
\n'); diff --git a/src/subParsers/tables.js b/src/subParsers/tables.js index dcd74c9..6ce7bc6 100644 --- a/src/subParsers/tables.js +++ b/src/subParsers/tables.js @@ -22,7 +22,8 @@ showdown.subParser('tables', function (text, options, globals) { function parseHeaders (header, style) { var id = ''; header = header.trim(); - if (options.tableHeaderId) { + // support both tablesHeaderId and tableHeaderId due to error in documention so we don't break backwards compatibility + if (options.tablesHeaderId || options.tableHeaderId) { id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"'; } header = showdown.subParser('spanGamut')(header, options, globals); diff --git a/test/features/#374.escape-html-tags.html b/test/features/#374.escape-html-tags.html new file mode 100644 index 0000000..25126a6 --- /dev/null +++ b/test/features/#374.escape-html-tags.html @@ -0,0 +1 @@ +

<div>foo</div>

\ No newline at end of file diff --git a/test/features/#374.escape-html-tags.md b/test/features/#374.escape-html-tags.md new file mode 100644 index 0000000..07d5f92 --- /dev/null +++ b/test/features/#374.escape-html-tags.md @@ -0,0 +1 @@ +\
foo\
diff --git a/test/issues/#397.unordered-list-strange-behavior.html b/test/issues/#397.unordered-list-strange-behavior.html new file mode 100644 index 0000000..bab378d --- /dev/null +++ b/test/issues/#397.unordered-list-strange-behavior.html @@ -0,0 +1,14 @@ + diff --git a/test/issues/#397.unordered-list-strange-behavior.md b/test/issues/#397.unordered-list-strange-behavior.md new file mode 100644 index 0000000..59bc46b --- /dev/null +++ b/test/issues/#397.unordered-list-strange-behavior.md @@ -0,0 +1,11 @@ +- **Customer** – Opens the Customer List. Refer to the document “Customer Management”. + - Customer List + - New Customer + - Customer Prices + - Appointments + +- **Designer** - Opens the Designer List. Refer to the document “Designer Commissions”. + - Designer List + - New Designer + - Designer Payment List + - New Designer Payment \ No newline at end of file diff --git a/test/node/testsuite.features.js b/test/node/testsuite.features.js index 25907eb..b4234ad 100644 --- a/test/node/testsuite.features.js +++ b/test/node/testsuite.features.js @@ -80,6 +80,8 @@ describe('makeHtml() features testsuite', function () { converter = new showdown.Converter({customizedHeaderId: true}); } else if (testsuite[i].name === '#378.simplifiedAutoLinks-with-excludeTrailingPunctuationFromURLs') { converter = new showdown.Converter({simplifiedAutoLink: true, excludeTrailingPunctuationFromURLs: true}); + } else if (testsuite[i].name === '#374.escape-html-tags') { + converter = new showdown.Converter({backslashEscapesHTMLTags: true}); } else if (testsuite[i].name === '#379.openLinksInNewWindow-breaks-em-markdup') { converter = new showdown.Converter({openLinksInNewWindow: true}); } else { @@ -95,7 +97,7 @@ describe('makeHtml() features testsuite', function () { suite = tableSuite; for (var i = 0; i < suite.length; ++i) { if (suite[i].name === 'basic-with-header-ids') { - converter = new showdown.Converter({tables: true, tableHeaderId: true}); + converter = new showdown.Converter({tables: true, tablesHeaderId: true}); } else if (suite[i].name === '#179.parse-md-in-table-ths') { converter = new showdown.Converter({tables: true, strikethrough: true}); } else {