Browse Source

Add validation mode to all inline rules

pull/14/head
Alex Kocharin 10 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,
max = state.posMax,
oldPos = state.pos,
oldLength = state.tokens.length,
oldPending = state.pending,
oldFlag = state.validateInsideLink;
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) {
@ -52,8 +50,6 @@ function parseLinkLabel(state, start) {
// restore old state
state.pos = oldPos;
state.tokens.length = oldLength;
state.pending = oldPending;
state.validateInsideLink = oldFlag;
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
//
ParserInline.prototype.tokenizeSingle = function (state) {
ParserInline.prototype.skipToken = function (state) {
var ok, i,
rules = this._rules,
len = this._rules.length;
for (i = 0; i < len; i++) {
ok = rules[i](state);
ok = rules[i](state, true);
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]*)>/;
module.exports = function autolink(state) {
module.exports = function autolink(state, silent) {
var tail, linkMatch, emailMatch, url, pos = state.pos;
if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; }
@ -28,17 +28,19 @@ module.exports = function autolink(state) {
if (!state.parser.validateLink(url)) { return false; }
state.push({
type: 'link_open',
href: url,
level: state.level
});
state.push({
type: 'text',
content: escapeHtml(url),
level: state.level + 1
});
state.push({ type: 'link_close', level: state.level });
if (!silent) {
state.push({
type: 'link_open',
href: url,
level: state.level
});
state.push({
type: 'text',
content: escapeHtml(url),
level: state.level + 1
});
state.push({ type: 'link_close', level: state.level });
}
state.pos += linkMatch[0].length;
return true;
@ -52,17 +54,19 @@ module.exports = function autolink(state) {
if (!state.parser.validateLink('mailto:' + url)) { return false; }
state.push({
type: 'link_open',
href: 'mailto:' + url,
level: state.level
});
state.push({
type: 'text',
content: escapeHtml(url),
level: state.level + 1
});
state.push({ type: 'link_close', level: state.level });
if (!silent) {
state.push({
type: 'link_open',
href: 'mailto:' + url,
level: state.level
});
state.push({
type: 'text',
content: escapeHtml(url),
level: state.level + 1
});
state.push({ type: 'link_close', level: state.level });
}
state.pos += emailMatch[0].length;
return true;

22
lib/rules_inline/backticks.js

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

41
lib/rules_inline/emphasis.js

@ -91,11 +91,9 @@ function parseEnd(state, start) {
return count;
}
module.exports = function emphasis(state/*, silent*/) {
module.exports = function emphasis(state, silent) {
var startCount,
count,
oldLength,
oldPending,
oldFlag,
found,
ok,
@ -124,8 +122,6 @@ module.exports = function emphasis(state/*, silent*/) {
if (state.level >= state.options.maxNesting) { return false; }
oldLength = state.tokens.length;
oldPending = state.pending;
oldFlag = state.validateInsideEm;
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) {
haveLiteralAsterisk = false;
} else {
haveLiteralAsterisk = state.src.charCodeAt(state.pos) === marker;
state.pending += state.src[state.pos];
state.pos++;
}
}
// restore old state
state.tokens.length = oldLength;
state.pending = oldPending;
state.validateInsideEm = oldFlag;
if (!found) {
@ -193,23 +186,25 @@ module.exports = function emphasis(state/*, silent*/) {
}
// found!
state.posMax = state.pos;
state.pos = start + startCount;
if (!silent) {
state.posMax = state.pos;
state.pos = start + startCount;
if (startCount === 2 || startCount === 3) {
state.push({ type: 'strong_open', level: state.level++ });
}
if (startCount === 1 || startCount === 3) {
state.push({ type: 'em_open', level: state.level++ });
}
if (startCount === 2 || startCount === 3) {
state.push({ type: 'strong_open', level: state.level++ });
}
if (startCount === 1 || startCount === 3) {
state.push({ type: 'em_open', level: state.level++ });
}
state.parser.tokenize(state);
state.parser.tokenize(state);
if (startCount === 1 || startCount === 3) {
state.push({ type: 'em_close', level: --state.level });
}
if (startCount === 2 || startCount === 3) {
state.push({ type: 'strong_close', level: --state.level });
if (startCount === 1 || startCount === 3) {
state.push({ type: 'em_close', level: --state.level });
}
if (startCount === 2 || startCount === 3) {
state.push({ type: 'strong_close', level: --state.level });
}
}
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;
module.exports = function entity(state) {
module.exports = function entity(state, silent) {
var ch, code, match, pos = state.pos, max = state.posMax;
if (state.src.charCodeAt(pos) !== 0x26/* & */) { return false; }
@ -23,8 +23,10 @@ module.exports = function entity(state) {
if (ch === 0x23 /* # */) {
match = state.src.slice(pos).match(DIGITAL_RE);
if (match) {
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);
if (!silent) {
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;
return true;
}
@ -32,7 +34,7 @@ module.exports = function entity(state) {
match = state.src.slice(pos).match(NAMED_RE);
if (match) {
if (entities.hasOwnProperty(match[1])) {
state.pending += escapeHtml(entities[match[1]]);
if (!silent) { state.pending += escapeHtml(entities[match[1]]); }
state.pos += match[0].length;
return true;
}
@ -40,7 +42,7 @@ module.exports = function entity(state) {
}
}
state.pending += '&amp;';
if (!silent) { state.pending += '&amp;'; }
state.pos++;
return true;
};

27
lib/rules_inline/escape.js

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

12
lib/rules_inline/escape_html_char.js

@ -1,18 +1,20 @@
// Process < > " (& was processed in markdown escape)
module.exports = function escape_html_char(state) {
var ch = state.src.charCodeAt(state.pos);
module.exports = function escape_html_char(state, silent) {
var ch = state.src.charCodeAt(state.pos),
str;
if (ch === 0x3C/* < */) {
state.pending += '&lt;';
str = '&lt;';
} else if (ch === 0x3E/* > */) {
state.pending += '&gt;';
str = '&gt;';
} else if (ch === 0x22/* " */) {
state.pending += '&quot;';
str = '&quot;';
} else {
return false;
}
if (!silent) { state.pending += str; }
state.pos++;
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;
if (!state.options.html) { return false; }
@ -37,12 +37,13 @@ module.exports = function htmltag(state) {
match = state.src.slice(pos).match(HTML_TAG_RE);
if (!match) { return false; }
state.push({
type: 'htmltag',
content: state.src.slice(pos, pos + match[0].length),
level: state.level
});
//console.log(state.tokens)
if (!silent) {
state.push({
type: 'htmltag',
content: state.src.slice(pos, pos + match[0].length),
level: state.level
});
}
state.pos += match[0].length;
return true;
};

52
lib/rules_inline/links.js

@ -8,7 +8,7 @@ var parseLinkTitle = require('../links').parseLinkTitle;
var normalizeReference = require('../links').normalizeReference;
function links(state) {
module.exports = function links(state, silent) {
var labelStart,
labelEnd,
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;
// so all that's left to do is to call tokenizer.
//
state.pos = labelStart;
state.posMax = labelEnd;
if (isImage) {
state.push({
type: 'image',
src: href,
title: title,
alt: state.src.substr(labelStart, labelEnd - labelStart),
level: state.level
});
} else {
state.push({
type: 'link_open',
href: href,
title: title,
level: state.level++
});
state.linkLevel++;
state.parser.tokenize(state);
state.linkLevel--;
state.push({ type: 'link_close', level: --state.level });
if (!silent) {
state.pos = labelStart;
state.posMax = labelEnd;
if (isImage) {
state.push({
type: 'image',
src: href,
title: title,
alt: state.src.substr(labelStart, labelEnd - labelStart),
level: state.level
});
} else {
state.push({
type: 'link_open',
href: href,
title: title,
level: state.level++
});
state.linkLevel++;
state.parser.tokenize(state);
state.linkLevel--;
state.push({ type: 'link_close', level: --state.level });
}
}
state.pos = pos;
state.posMax = max;
return true;
}
module.exports = links;
};

32
lib/rules_inline/newline.js

@ -1,6 +1,6 @@
// Proceess '\n'
module.exports = function newline(state) {
module.exports = function newline(state, silent) {
var pmax, max, pos = state.pos;
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!
// Pending string is stored in concat mode, indexed lookups will cause
// convertion to flat mode.
if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) {
if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) {
state.pending = state.pending.replace(/ +$/, '');
state.push({
type: 'hardbreak',
level: state.level
});
if (!silent) {
if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) {
if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) {
state.pending = state.pending.replace(/ +$/, '');
state.push({
type: 'hardbreak',
level: state.level
});
} else {
state.pending = state.pending.slice(0, -1);
state.push({
type: 'softbreak',
level: state.level
});
}
} else {
state.pending = state.pending.slice(0, -1);
state.push({
type: 'softbreak',
level: state.level
});
}
} else {
state.push({
type: 'softbreak',
level: state.level
});
}
pos++;

29
lib/rules_inline/strikethrough.js

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

4
lib/rules_inline/text.js

@ -1,12 +1,12 @@
// Skip text characters for text token, place those to pendibg buffer
// 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);
if (!match) { return false; }
state.pending += match[0];
if (!silent) { state.pending += match[0]; }
state.pos += match[0].length;
return true;

Loading…
Cancel
Save