Browse Source

Add validation mode to all inline rules

pull/14/head
Alex Kocharin 11 years ago
parent
commit
5d772b9bb6
  1. 8
      lib/links.js
  2. 6
      lib/parser_inline.js
  3. 50
      lib/rules_inline/autolink.js
  4. 22
      lib/rules_inline/backticks.js
  5. 41
      lib/rules_inline/emphasis.js
  6. 12
      lib/rules_inline/entity.js
  7. 27
      lib/rules_inline/escape.js
  8. 12
      lib/rules_inline/escape_html_char.js
  9. 15
      lib/rules_inline/htmltag.js
  10. 52
      lib/rules_inline/links.js
  11. 32
      lib/rules_inline/newline.js
  12. 29
      lib/rules_inline/strikethrough.js
  13. 4
      lib/rules_inline/text.js

8
lib/links.js

@ -11,8 +11,6 @@ function parseLinkLabel(state, start) {
labelEnd = -1, labelEnd = -1,
max = state.posMax, max = state.posMax,
oldPos = state.pos, oldPos = state.pos,
oldLength = state.tokens.length,
oldPending = state.pending,
oldFlag = state.validateInsideLink; oldFlag = state.validateInsideLink;
if (state.validateInsideLink) { return -1; } if (state.validateInsideLink) { return -1; }
@ -38,9 +36,9 @@ function parseLinkLabel(state, start) {
} }
} }
ok = state.parser.tokenizeSingle(state); ok = state.parser.skipToken(state);
if (!ok) { state.pending += state.src[state.pos++]; } if (!ok) { state.pos++; }
} }
if (found) { if (found) {
@ -52,8 +50,6 @@ function parseLinkLabel(state, start) {
// restore old state // restore old state
state.pos = oldPos; state.pos = oldPos;
state.tokens.length = oldLength;
state.pending = oldPending;
state.validateInsideLink = oldFlag; state.validateInsideLink = oldFlag;
return labelEnd; return labelEnd;

6
lib/parser_inline.js

@ -57,16 +57,16 @@ ParserInline.prototype.rulesUpdate = function () {
}; };
// Generate single token; // Skip single token by running all rules in validation mode;
// returns `true` if any rule reported success // returns `true` if any rule reported success
// //
ParserInline.prototype.tokenizeSingle = function (state) { ParserInline.prototype.skipToken = function (state) {
var ok, i, var ok, i,
rules = this._rules, rules = this._rules,
len = this._rules.length; len = this._rules.length;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
ok = rules[i](state); ok = rules[i](state, true);
if (ok) { break; } if (ok) { break; }
} }

50
lib/rules_inline/autolink.js

@ -10,7 +10,7 @@ var EMAIL_RE = /^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9
var AUTOLINK_RE = /^<([a-zA-Z.\-]{1,25}):([^<>\x00-\x20]*)>/; var AUTOLINK_RE = /^<([a-zA-Z.\-]{1,25}):([^<>\x00-\x20]*)>/;
module.exports = function autolink(state) { module.exports = function autolink(state, silent) {
var tail, linkMatch, emailMatch, url, pos = state.pos; var tail, linkMatch, emailMatch, url, pos = state.pos;
if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; } if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; }
@ -28,17 +28,19 @@ module.exports = function autolink(state) {
if (!state.parser.validateLink(url)) { return false; } if (!state.parser.validateLink(url)) { return false; }
state.push({ if (!silent) {
type: 'link_open', state.push({
href: url, type: 'link_open',
level: state.level href: url,
}); level: state.level
state.push({ });
type: 'text', state.push({
content: escapeHtml(url), type: 'text',
level: state.level + 1 content: escapeHtml(url),
}); level: state.level + 1
state.push({ type: 'link_close', level: state.level }); });
state.push({ type: 'link_close', level: state.level });
}
state.pos += linkMatch[0].length; state.pos += linkMatch[0].length;
return true; return true;
@ -52,17 +54,19 @@ module.exports = function autolink(state) {
if (!state.parser.validateLink('mailto:' + url)) { return false; } if (!state.parser.validateLink('mailto:' + url)) { return false; }
state.push({ if (!silent) {
type: 'link_open', state.push({
href: 'mailto:' + url, type: 'link_open',
level: state.level href: 'mailto:' + url,
}); level: state.level
state.push({ });
type: 'text', state.push({
content: escapeHtml(url), type: 'text',
level: state.level + 1 content: escapeHtml(url),
}); level: state.level + 1
state.push({ type: 'link_close', level: state.level }); });
state.push({ type: 'link_close', level: state.level });
}
state.pos += emailMatch[0].length; state.pos += emailMatch[0].length;
return true; return true;

22
lib/rules_inline/backticks.js

@ -1,6 +1,6 @@
// Parse backticks // Parse backticks
module.exports = function backticks(state) { module.exports = function backticks(state, silent) {
var start, max, marker, matchStart, matchEnd, var start, max, marker, matchStart, matchEnd,
pos = state.pos, pos = state.pos,
ch = state.src.charCodeAt(pos); ch = state.src.charCodeAt(pos);
@ -23,20 +23,22 @@ module.exports = function backticks(state) {
while (matchEnd < max && state.src.charCodeAt(matchEnd) === 0x60/* ` */) { matchEnd++; } while (matchEnd < max && state.src.charCodeAt(matchEnd) === 0x60/* ` */) { matchEnd++; }
if (matchEnd - matchStart === marker.length) { if (matchEnd - matchStart === marker.length) {
state.push({ if (!silent) {
type: 'code', state.push({
content: state.src.slice(pos, matchStart) type: 'code',
.replace(/[ \n]+/g,' ') content: state.src.slice(pos, matchStart)
.trim(), .replace(/[ \n]+/g,' ')
block: false, .trim(),
level: state.level block: false,
}); level: state.level
});
}
state.pos = matchEnd; state.pos = matchEnd;
return true; return true;
} }
} }
state.pending += marker; if (!silent) { state.pending += marker; }
state.pos += marker.length; state.pos += marker.length;
return true; return true;
}; };

41
lib/rules_inline/emphasis.js

@ -91,11 +91,9 @@ function parseEnd(state, start) {
return count; return count;
} }
module.exports = function emphasis(state/*, silent*/) { module.exports = function emphasis(state, silent) {
var startCount, var startCount,
count, count,
oldLength,
oldPending,
oldFlag, oldFlag,
found, found,
ok, ok,
@ -124,8 +122,6 @@ module.exports = function emphasis(state/*, silent*/) {
if (state.level >= state.options.maxNesting) { return false; } if (state.level >= state.options.maxNesting) { return false; }
oldLength = state.tokens.length;
oldPending = state.pending;
oldFlag = state.validateInsideEm; oldFlag = state.validateInsideEm;
state.pos = start + startCount; state.pos = start + startCount;
@ -170,20 +166,17 @@ module.exports = function emphasis(state/*, silent*/) {
} }
} }
ok = state.parser.tokenizeSingle(state); ok = state.parser.skipToken(state);
if (ok) { if (ok) {
haveLiteralAsterisk = false; haveLiteralAsterisk = false;
} else { } else {
haveLiteralAsterisk = state.src.charCodeAt(state.pos) === marker; haveLiteralAsterisk = state.src.charCodeAt(state.pos) === marker;
state.pending += state.src[state.pos];
state.pos++; state.pos++;
} }
} }
// restore old state // restore old state
state.tokens.length = oldLength;
state.pending = oldPending;
state.validateInsideEm = oldFlag; state.validateInsideEm = oldFlag;
if (!found) { if (!found) {
@ -193,23 +186,25 @@ module.exports = function emphasis(state/*, silent*/) {
} }
// found! // found!
state.posMax = state.pos; if (!silent) {
state.pos = start + startCount; state.posMax = state.pos;
state.pos = start + startCount;
if (startCount === 2 || startCount === 3) { if (startCount === 2 || startCount === 3) {
state.push({ type: 'strong_open', level: state.level++ }); state.push({ type: 'strong_open', level: state.level++ });
} }
if (startCount === 1 || startCount === 3) { if (startCount === 1 || startCount === 3) {
state.push({ type: 'em_open', level: state.level++ }); state.push({ type: 'em_open', level: state.level++ });
} }
state.parser.tokenize(state); state.parser.tokenize(state);
if (startCount === 1 || startCount === 3) { if (startCount === 1 || startCount === 3) {
state.push({ type: 'em_close', level: --state.level }); state.push({ type: 'em_close', level: --state.level });
} }
if (startCount === 2 || startCount === 3) { if (startCount === 2 || startCount === 3) {
state.push({ type: 'strong_close', level: --state.level }); state.push({ type: 'strong_close', level: --state.level });
}
} }
state.pos = state.posMax + startCount; state.pos = state.posMax + startCount;

12
lib/rules_inline/entity.js

@ -12,7 +12,7 @@ var DIGITAL_RE = /^&#((?:x[a-f0-9]{1,8}|[0-9]{1,8}));/i;
var NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i; var NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i;
module.exports = function entity(state) { module.exports = function entity(state, silent) {
var ch, code, match, pos = state.pos, max = state.posMax; var ch, code, match, pos = state.pos, max = state.posMax;
if (state.src.charCodeAt(pos) !== 0x26/* & */) { return false; } if (state.src.charCodeAt(pos) !== 0x26/* & */) { return false; }
@ -23,8 +23,10 @@ module.exports = function entity(state) {
if (ch === 0x23 /* # */) { if (ch === 0x23 /* # */) {
match = state.src.slice(pos).match(DIGITAL_RE); match = state.src.slice(pos).match(DIGITAL_RE);
if (match) { if (match) {
code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10); if (!silent) {
state.pending += isValidEntityCode(code) ? escapeHtml(fromCodePoint(code)) : fromCodePoint(0xFFFD); code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10);
state.pending += isValidEntityCode(code) ? escapeHtml(fromCodePoint(code)) : fromCodePoint(0xFFFD);
}
state.pos += match[0].length; state.pos += match[0].length;
return true; return true;
} }
@ -32,7 +34,7 @@ module.exports = function entity(state) {
match = state.src.slice(pos).match(NAMED_RE); match = state.src.slice(pos).match(NAMED_RE);
if (match) { if (match) {
if (entities.hasOwnProperty(match[1])) { if (entities.hasOwnProperty(match[1])) {
state.pending += escapeHtml(entities[match[1]]); if (!silent) { state.pending += escapeHtml(entities[match[1]]); }
state.pos += match[0].length; state.pos += match[0].length;
return true; return true;
} }
@ -40,7 +42,7 @@ module.exports = function entity(state) {
} }
} }
state.pending += '&amp;'; if (!silent) { state.pending += '&amp;'; }
state.pos++; state.pos++;
return true; return true;
}; };

27
lib/rules_inline/escape.js

@ -5,8 +5,8 @@ var ESCAPED = {};
'\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-' '\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-'
.split('').forEach(function(ch) { ESCAPED[ch.charCodeAt(0)] = true; }); .split('').forEach(function(ch) { ESCAPED[ch.charCodeAt(0)] = true; });
module.exports = function escape(state) { module.exports = function escape(state, silent) {
var ch, pos = state.pos, max = state.posMax; var ch, str, pos = state.pos, max = state.posMax;
if (state.src.charCodeAt(pos) !== 0x5C/* \ */) { return false; } if (state.src.charCodeAt(pos) !== 0x5C/* \ */) { return false; }
@ -18,25 +18,28 @@ module.exports = function escape(state) {
if (typeof ESCAPED[ch] !== 'undefined') { if (typeof ESCAPED[ch] !== 'undefined') {
// escape html chars if needed // escape html chars if needed
if (ch === 0x26/* & */) { if (ch === 0x26/* & */) {
state.pending += '&amp;'; str = '&amp;';
} else if (ch === 0x3C/* < */) { } else if (ch === 0x3C/* < */) {
state.pending += '&lt;'; str = '&lt;';
} else if (ch === 0x3E/* > */) { } else if (ch === 0x3E/* > */) {
state.pending += '&gt;'; str = '&gt;';
} else if (ch === 0x22/* " */) { } else if (ch === 0x22/* " */) {
state.pending += '&quot;'; str = '&quot;';
} else { } else {
state.pending += state.src[pos]; str = state.src[pos];
} }
if (!silent) { state.pending += str; }
state.pos += 2; state.pos += 2;
return true; return true;
} }
if (ch === 0x0A) { if (ch === 0x0A) {
state.push({ if (!silent) {
type: 'hardbreak', state.push({
level: state.level type: 'hardbreak',
}); level: state.level
});
}
pos++; pos++;
// skip leading whitespaces from next line // skip leading whitespaces from next line
@ -47,7 +50,7 @@ module.exports = function escape(state) {
} }
} }
state.pending += '\\'; if (!silent) { state.pending += '\\'; }
state.pos++; state.pos++;
return true; return true;
}; };

12
lib/rules_inline/escape_html_char.js

@ -1,18 +1,20 @@
// Process < > " (& was processed in markdown escape) // Process < > " (& was processed in markdown escape)
module.exports = function escape_html_char(state) { module.exports = function escape_html_char(state, silent) {
var ch = state.src.charCodeAt(state.pos); var ch = state.src.charCodeAt(state.pos),
str;
if (ch === 0x3C/* < */) { if (ch === 0x3C/* < */) {
state.pending += '&lt;'; str = '&lt;';
} else if (ch === 0x3E/* > */) { } else if (ch === 0x3E/* > */) {
state.pending += '&gt;'; str = '&gt;';
} else if (ch === 0x22/* " */) { } else if (ch === 0x22/* " */) {
state.pending += '&quot;'; str = '&quot;';
} else { } else {
return false; return false;
} }
if (!silent) { state.pending += str; }
state.pos++; state.pos++;
return true; return true;
}; };

15
lib/rules_inline/htmltag.js

@ -13,7 +13,7 @@ function isLetter(ch) {
} }
module.exports = function htmltag(state) { module.exports = function htmltag(state, silent) {
var ch, match, max, pos = state.pos; var ch, match, max, pos = state.pos;
if (!state.options.html) { return false; } if (!state.options.html) { return false; }
@ -37,12 +37,13 @@ module.exports = function htmltag(state) {
match = state.src.slice(pos).match(HTML_TAG_RE); match = state.src.slice(pos).match(HTML_TAG_RE);
if (!match) { return false; } if (!match) { return false; }
state.push({ if (!silent) {
type: 'htmltag', state.push({
content: state.src.slice(pos, pos + match[0].length), type: 'htmltag',
level: state.level content: state.src.slice(pos, pos + match[0].length),
}); level: state.level
//console.log(state.tokens) });
}
state.pos += match[0].length; state.pos += match[0].length;
return true; return true;
}; };

52
lib/rules_inline/links.js

@ -8,7 +8,7 @@ var parseLinkTitle = require('../links').parseLinkTitle;
var normalizeReference = require('../links').normalizeReference; var normalizeReference = require('../links').normalizeReference;
function links(state) { module.exports = function links(state, silent) {
var labelStart, var labelStart,
labelEnd, labelEnd,
label, label,
@ -132,33 +132,33 @@ function links(state) {
// We found the end of the link, and know for a fact it's a valid link; // We found the end of the link, and know for a fact it's a valid link;
// so all that's left to do is to call tokenizer. // so all that's left to do is to call tokenizer.
// //
state.pos = labelStart; if (!silent) {
state.posMax = labelEnd; state.pos = labelStart;
state.posMax = labelEnd;
if (isImage) {
state.push({ if (isImage) {
type: 'image', state.push({
src: href, type: 'image',
title: title, src: href,
alt: state.src.substr(labelStart, labelEnd - labelStart), title: title,
level: state.level alt: state.src.substr(labelStart, labelEnd - labelStart),
}); level: state.level
} else { });
state.push({ } else {
type: 'link_open', state.push({
href: href, type: 'link_open',
title: title, href: href,
level: state.level++ title: title,
}); level: state.level++
state.linkLevel++; });
state.parser.tokenize(state); state.linkLevel++;
state.linkLevel--; state.parser.tokenize(state);
state.push({ type: 'link_close', level: --state.level }); state.linkLevel--;
state.push({ type: 'link_close', level: --state.level });
}
} }
state.pos = pos; state.pos = pos;
state.posMax = max; state.posMax = max;
return true; return true;
} };
module.exports = links;

32
lib/rules_inline/newline.js

@ -1,6 +1,6 @@
// Proceess '\n' // Proceess '\n'
module.exports = function newline(state) { module.exports = function newline(state, silent) {
var pmax, max, pos = state.pos; var pmax, max, pos = state.pos;
if (state.src.charCodeAt(pos) !== 0x0A/* \n */) { return false; } if (state.src.charCodeAt(pos) !== 0x0A/* \n */) { return false; }
@ -12,26 +12,28 @@ module.exports = function newline(state) {
// Lookup in pending chars is bad practice! Don't copy to other rules! // Lookup in pending chars is bad practice! Don't copy to other rules!
// Pending string is stored in concat mode, indexed lookups will cause // Pending string is stored in concat mode, indexed lookups will cause
// convertion to flat mode. // convertion to flat mode.
if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) { if (!silent) {
if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) { if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) {
state.pending = state.pending.replace(/ +$/, ''); if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) {
state.push({ state.pending = state.pending.replace(/ +$/, '');
type: 'hardbreak', state.push({
level: state.level type: 'hardbreak',
}); level: state.level
});
} else {
state.pending = state.pending.slice(0, -1);
state.push({
type: 'softbreak',
level: state.level
});
}
} else { } else {
state.pending = state.pending.slice(0, -1);
state.push({ state.push({
type: 'softbreak', type: 'softbreak',
level: state.level level: state.level
}); });
} }
} else {
state.push({
type: 'softbreak',
level: state.level
});
} }
pos++; pos++;

29
lib/rules_inline/strikethrough.js

@ -2,10 +2,8 @@
'use strict'; 'use strict';
module.exports = function strikethrough(state) { module.exports = function strikethrough(state, silent) {
var oldLength, var oldFlag,
oldPending,
oldFlag,
found, found,
ok, ok,
pos, pos,
@ -37,12 +35,10 @@ module.exports = function strikethrough(state) {
if (pos !== start + 2) { if (pos !== start + 2) {
// sequence of 3+ markers taking as literal, same as in a emphasis // sequence of 3+ markers taking as literal, same as in a emphasis
state.pos += pos - start; state.pos += pos - start;
state.pending += state.src.slice(start, pos); if (!silent) { state.pending += state.src.slice(start, pos); }
return true; return true;
} }
oldLength = state.tokens.length;
oldPending = state.pending;
oldFlag = state.validateInsideEm; oldFlag = state.validateInsideEm;
state.pos = start + 2; state.pos = start + 2;
@ -72,17 +68,14 @@ module.exports = function strikethrough(state) {
} }
} }
ok = state.parser.tokenizeSingle(state); ok = state.parser.skipToken(state);
if (!ok) { if (!ok) {
state.pending += state.src[state.pos];
state.pos++; state.pos++;
} }
} }
// restore old state // restore old state
state.tokens.length = oldLength;
state.pending = oldPending;
state.validateInsideEm = oldFlag; state.validateInsideEm = oldFlag;
if (!found) { if (!found) {
@ -91,13 +84,15 @@ module.exports = function strikethrough(state) {
return false; return false;
} }
// found! if (!silent) {
state.posMax = state.pos; // found!
state.pos = start + 2; state.posMax = state.pos;
state.pos = start + 2;
state.push({ type: 'del_open', level: state.level++ }); state.push({ type: 'del_open', level: state.level++ });
state.parser.tokenize(state); state.parser.tokenize(state);
state.push({ type: 'del_close', level: --state.level }); state.push({ type: 'del_close', level: --state.level });
}
state.pos = state.posMax + 2; state.pos = state.posMax + 2;
state.posMax = max; state.posMax = max;

4
lib/rules_inline/text.js

@ -1,12 +1,12 @@
// Skip text characters for text token, place those to pendibg buffer // Skip text characters for text token, place those to pendibg buffer
// and increment current pos // and increment current pos
module.exports = function text(state) { module.exports = function text(state, silent) {
var match = state.src.slice(state.pos).match(state.parser.textMatch); var match = state.src.slice(state.pos).match(state.parser.textMatch);
if (!match) { return false; } if (!match) { return false; }
state.pending += match[0]; if (!silent) { state.pending += match[0]; }
state.pos += match[0].length; state.pos += match[0].length;
return true; return true;

Loading…
Cancel
Save