diff --git a/lib/helpers.js b/lib/helpers.js index cf910f7..623a822 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -91,6 +91,23 @@ function escapeHtml(str) { return str; } +function escapeHtmlKeepEntities(str) { + if (str.indexOf('&') >= 0) { + str = str.replace(/[&](?![#](x[a-f0-9]{1,8}|[0-9]{1,8});|[a-z][a-z0-9]{1,31};)/gi,'&'); + } + if (str.indexOf('<') >= 0) { str = str.replace(/') >= 0) { str = str.replace(/>/g, '>'); } + if (str.indexOf('"') >= 0) { str = str.replace(/"/g, '"'); } + return str; +} + +var UNESCAPE_MD_RE = /\\([\\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g; + +function unescapeMd(str) { + if (str.indexOf('\\') < 0) { return str; } + return str.replace(UNESCAPE_MD_RE, '$1'); +} + exports.isWhiteSpace = isWhiteSpace; exports.isEmpty = isEmpty; @@ -100,3 +117,5 @@ exports.skipChars = skipChars; exports.getLines = getLines; exports.skipCharsBack = skipCharsBack; exports.escapeHtml = escapeHtml; +exports.unescapeMd = unescapeMd; +exports.escapeHtmlKeepEntities = escapeHtmlKeepEntities; diff --git a/lib/lexer_block/fences.js b/lib/lexer_block/fences.js index 6e69115..b9d6048 100644 --- a/lib/lexer_block/fences.js +++ b/lib/lexer_block/fences.js @@ -85,7 +85,7 @@ module.exports = function fences(state, startLine, endLine, silent) { state.tokens.push({ type: 'fence', - params: params ? params.split(/\s+/g) : [], + params: params, content: getLines(state, startLine + 1, nextLine, len, true) }); diff --git a/lib/lexer_block/heading.js b/lib/lexer_block/heading.js index cb29f54..caf6530 100644 --- a/lib/lexer_block/heading.js +++ b/lib/lexer_block/heading.js @@ -55,7 +55,7 @@ module.exports = function heading(state, startLine, endLine, silent) { if (pos < max) { state.tokens.push({ type: 'inline', - content: state.src.slice(pos, max) + content: state.src.slice(pos, max).trim() }); } state.tokens.push({ type: 'heading_close', level: level }); diff --git a/lib/lexer_block/lheading.js b/lib/lexer_block/lheading.js index 13953d1..00c922b 100644 --- a/lib/lexer_block/lheading.js +++ b/lib/lexer_block/lheading.js @@ -40,7 +40,7 @@ module.exports = function lheading(state, startLine, endLine, silent) { state.tokens.push({ type: 'heading_open', level: marker === 0x3D/* = */ ? 1 : 2 }); state.tokens.push({ type: 'inline', - content: state.src.slice(pos, max) + content: state.src.slice(pos, max).trim() }); state.tokens.push({ type: 'heading_close', level: marker === 0x3D/* = */ ? 1 : 2 }); diff --git a/lib/lexer_block/paragraph.js b/lib/lexer_block/paragraph.js index 0af02e3..4f67808 100644 --- a/lib/lexer_block/paragraph.js +++ b/lib/lexer_block/paragraph.js @@ -37,7 +37,7 @@ module.exports = function paragraph(state, startLine/*, endLine*/) { state.tokens.push({ type: 'paragraph_open' }); state.tokens.push({ type: 'inline', - content: getLines(state, startLine, nextLine, state.blkIndent, false) + content: getLines(state, startLine, nextLine, state.blkIndent, false).trim() }); state.tokens.push({ type: 'paragraph_close' }); diff --git a/lib/lexer_inline.js b/lib/lexer_inline.js index ee622f1..f48566d 100644 --- a/lib/lexer_inline.js +++ b/lib/lexer_inline.js @@ -152,21 +152,10 @@ LexerInline.prototype.tokenize = function (state) { // Parse input string. // LexerInline.prototype.parse = function (str, options) { - var state = new StateInline(str, this, options), - tokens = state.tokens, - len, tok; + var state = new StateInline(str, this, options); this.tokenize(state); - // If last token is text - strip tailing spaces - len = tokens.length; - if (len) { - tok = tokens[len - 1]; - if (tok.type === 'text' && tok.content.charCodeAt(tok.content.length - 1) === 0x20) { - tok.content = tok.content.replace(/ +$/, ''); - } - } - return state.tokens; }; diff --git a/lib/lexer_inline/autolink.js b/lib/lexer_inline/autolink.js index 4c3a1d1..e0fd672 100644 --- a/lib/lexer_inline/autolink.js +++ b/lib/lexer_inline/autolink.js @@ -1,7 +1,8 @@ // Process autolinks '' -var escape = require('../helpers').escapeHtml; +var escapeHtml = require('../helpers').escapeHtml; + /*eslint max-len:0*/ var EMAIL_RE = /^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/; @@ -24,11 +25,11 @@ module.exports = function autolink(state) { state.push({ type: 'link_open', - href: escape(linkMatch[0].slice(1, -1)) + href: linkMatch[0].slice(1, -1) }); state.push({ type: 'text', - content: escape(linkMatch[0].slice(1, -1)) + content: escapeHtml(linkMatch[0].slice(1, -1)) }); state.push({ type: 'link_close' }); @@ -41,11 +42,11 @@ module.exports = function autolink(state) { if (emailMatch) { state.tokens.push({ type: 'link_open', - href: 'mailto:' + escape(emailMatch[0].slice(1, -1)) + href: 'mailto:' + emailMatch[0].slice(1, -1) }); state.tokens.push({ type: 'text', - content: escape(emailMatch[0].slice(1, -1)) + content: escapeHtml(emailMatch[0].slice(1, -1)) }); state.tokens.push({ type: 'link_close' }); diff --git a/lib/lexer_inline/escape.js b/lib/lexer_inline/escape.js index 58a5801..345b853 100644 --- a/lib/lexer_inline/escape.js +++ b/lib/lexer_inline/escape.js @@ -1,6 +1,6 @@ // Proceess escaped chars and hardbreaks -var ESCAPED = '\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-]' +var ESCAPED = '\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-' .split('') .map(function(ch) { return ch.charCodeAt(0); }); diff --git a/lib/lexer_inline/newline.js b/lib/lexer_inline/newline.js index 1ae13e6..57d1639 100644 --- a/lib/lexer_inline/newline.js +++ b/lib/lexer_inline/newline.js @@ -26,7 +26,7 @@ module.exports = function escape(state) { } if (pmax > 0 && state.pending.charCodeAt(pmax) === 0x20) { - state.pending = state.pending.slice(0, -1); + state.pending = state.pending.replace(/ +$/, ''); } state.pending += '\n'; diff --git a/lib/renderer.js b/lib/renderer.js index 00b386f..8bfafb2 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -3,6 +3,8 @@ var assign = require('object-assign'); var escapeHtml = require('./helpers').escapeHtml; +var escapeHtmlKeepEntities = require('./helpers').escapeHtmlKeepEntities; +var unescapeMd = require('./helpers').unescapeMd; // check if we need to hide '\n' before next token @@ -40,9 +42,11 @@ rules.fence = function (tokens, idx, options) { var token = tokens[idx]; var langMark = ''; var langPrefix = options.codeLangPrefix || ''; + var params; - if (token.params.length) { - langMark = ' class="' + langPrefix + escapeHtml(token.params[0]) + '"'; + if (token.params) { + params = token.params.split(/ +/g); + langMark = ' class="' + langPrefix + escapeHtmlKeepEntities(unescapeMd(params[0])) + '"'; } return '
'
@@ -96,7 +100,7 @@ rules.paragraph_close = function (tokens, idx /*, options*/) {
 
 
 rules.link_open = function (tokens, idx /*, options*/) {
-  return '';
+  return '';
 };
 rules.link_close = function (/*tokens, idx, options*/) {
   return '';
diff --git a/lib/state_inline.js b/lib/state_inline.js
index c5e524d..ab840ad 100644
--- a/lib/state_inline.js
+++ b/lib/state_inline.js
@@ -16,11 +16,7 @@ function StateInline(src, lexer, options) {
 
 StateInline.prototype.pushText = function () {
   var pending = this.pending;
-  // strip leading spaces from the first token.
-  // others will be stripped by logic in `newline` rule
-  if (this.tokens.length === 0 && pending.charCodeAt(0) === 0x20) {
-    pending = pending.replace(/^ +/, '');
-  }
+
   this.tokens.push({
     type: 'text',
     content: pending
diff --git a/test/fixtures/stmd/bad.txt b/test/fixtures/stmd/bad.txt
index 7a3b44f..de9ac88 100644
--- a/test/fixtures/stmd/bad.txt
+++ b/test/fixtures/stmd/bad.txt
@@ -718,24 +718,6 @@ error:
 

[foo]: /bar* "ti*tle"

-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -src line: 3718 - -. -``` foo\+bar -foo -``` -. -
foo
-
-. - -error: - -
foo
-
- - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src line: 3791 @@ -781,24 +763,6 @@ error:

[foo]: /föö "föö"

-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -src line: 3811 - -. -``` föö -foo -``` -. -
foo
-
-. - -error: - -
foo
-
- - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src line: 3954 diff --git a/test/fixtures/stmd/good.txt b/test/fixtures/stmd/good.txt index 0ca42ad..ebccd9c 100644 --- a/test/fixtures/stmd/good.txt +++ b/test/fixtures/stmd/good.txt @@ -2668,6 +2668,18 @@ src line: 3688

http://google.com?find=\*

. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +src line: 3718 + +. +``` foo\+bar +foo +``` +. +
foo
+
+. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src line: 3736 @@ -2722,6 +2734,18 @@ src line: 3781

&MadeUpEntity;

. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +src line: 3811 + +. +``` föö +foo +``` +. +
foo
+
+. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src line: 3822