remarkable
Advanced tools
Comparing version
@@ -0,1 +1,10 @@ | ||
1.1.1 / 2014-10-22 | ||
------------------ | ||
- Fixed `Ruler.after()` method. | ||
- Fixed linkification | ||
- Simplified loose/tight rendering. | ||
- Refactored inline parser. No close coupled code in rules anymore. | ||
1.1.0 / 2014-10-20 | ||
@@ -2,0 +11,0 @@ ------------------ |
@@ -14,10 +14,8 @@ | ||
oldPos = state.pos, | ||
oldLength = state.tokens.length, | ||
oldPending = state.pending, | ||
oldFlag = state.validateInsideLink; | ||
oldFlag = state.isInLabel; | ||
if (state.validateInsideLink) { return -1; } | ||
if (state.isInLabel) { return -1; } | ||
if (state.label_nest_level) { | ||
state.label_nest_level--; | ||
if (state.labelUnmatchedScopes) { | ||
state.labelUnmatchedScopes--; | ||
return -1; | ||
@@ -27,3 +25,3 @@ } | ||
state.pos = start + 1; | ||
state.validateInsideLink = true; | ||
state.isInLabel = true; | ||
level = 1; | ||
@@ -43,5 +41,5 @@ | ||
ok = state.parser.tokenizeSingle(state); | ||
ok = state.parser.skipToken(state); | ||
if (!ok) { state.pending += state.src[state.pos++]; } | ||
if (!ok) { state.pos++; } | ||
} | ||
@@ -51,5 +49,5 @@ | ||
labelEnd = state.pos; | ||
state.label_nest_level = 0; | ||
state.labelUnmatchedScopes = 0; | ||
} else { | ||
state.label_nest_level = level - 1; | ||
state.labelUnmatchedScopes = level - 1; | ||
} | ||
@@ -59,5 +57,3 @@ | ||
state.pos = oldPos; | ||
state.tokens.length = oldLength; | ||
state.pending = oldPending; | ||
state.validateInsideLink = oldFlag; | ||
state.isInLabel = oldFlag; | ||
@@ -84,3 +80,3 @@ return labelEnd; | ||
state.pos = pos + 1; | ||
state.link_content = href; | ||
state.linkContent = href; | ||
return true; | ||
@@ -136,3 +132,3 @@ } | ||
state.pos = pos; | ||
state.link_content = href; | ||
state.linkContent = href; | ||
return true; | ||
@@ -163,3 +159,3 @@ } | ||
state.pos = pos + 1; | ||
state.link_content = title; | ||
state.linkContent = title; | ||
return true; | ||
@@ -166,0 +162,0 @@ } |
@@ -60,16 +60,23 @@ // Inline parser | ||
// 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) { | ||
var ok, i, | ||
ParserInline.prototype.skipToken = function (state) { | ||
var i, pos = state.pos, | ||
rules = this._rules, | ||
len = this._rules.length; | ||
if (state.cache[pos] !== undefined) { | ||
state.pos = state.cache[pos]; | ||
return true; | ||
} | ||
for (i = 0; i < len; i++) { | ||
ok = rules[i](state); | ||
if (ok) { break; } | ||
if (rules[i](state, true)) { | ||
state.cache[pos] = state.pos; | ||
return true; | ||
} | ||
} | ||
return ok; | ||
return false; | ||
}; | ||
@@ -96,3 +103,3 @@ | ||
for (i = 0; i < len; i++) { | ||
ok = rules[i](state); | ||
ok = rules[i](state, false); | ||
if (ok) { break; } | ||
@@ -99,0 +106,0 @@ } |
@@ -38,3 +38,3 @@ | ||
if (!parseLinkDestination(state, pos)) { return -1; } | ||
href = state.link_content; | ||
href = state.linkContent; | ||
pos = state.pos; | ||
@@ -53,3 +53,3 @@ | ||
if (pos < max && start !== pos && parseLinkTitle(state, pos)) { | ||
title = state.link_content; | ||
title = state.linkContent; | ||
pos = state.pos; | ||
@@ -56,0 +56,0 @@ } else { |
@@ -111,7 +111,7 @@ 'use strict'; | ||
rules.paragraph_open = function (/*tokens, idx, options*/) { | ||
return '<p>'; | ||
rules.paragraph_open = function (tokens, idx/*, options*/) { | ||
return tokens[idx].tight ? '' : '<p>'; | ||
}; | ||
rules.paragraph_close = function (tokens, idx /*, options*/) { | ||
return '</p>' + getBreak(tokens, idx); | ||
return (tokens[idx].tight ? '' : '</p>') + getBreak(tokens, idx); | ||
}; | ||
@@ -244,48 +244,11 @@ | ||
Renderer.prototype.render = function (tokens, options) { | ||
var i, len, name, | ||
var i, len, | ||
result = '', | ||
rules = this.rules, | ||
tightStack = []; | ||
rules = this.rules; | ||
// wrap paragraphs on top level by default | ||
var tight = false; | ||
for (i = 0, len = tokens.length; i < len; i++) { | ||
name = tokens[i].type; | ||
// Dirty stack machine to track lists style (loose/tight) | ||
if (name === 'ordered_list_open' || name === 'bullet_list_open') { | ||
tightStack.push(tight); | ||
tight = tokens[i].tight; | ||
} | ||
if (name === 'ordered_list_close' || name === 'bullet_list_close') { | ||
tight = tightStack.pop(); | ||
} | ||
if (name === 'blockquote_open') { | ||
tightStack.push(tight); | ||
tight = false; | ||
} | ||
if (name === 'blockquote_close') { | ||
tight = tightStack.pop(); | ||
} | ||
// in tight mode just ignore paragraphs for lists | ||
// TODO - track right nesting to blockquotes | ||
if (name === 'paragraph_open' && tight) { | ||
continue; | ||
} | ||
if (name === 'paragraph_close' && tight) { | ||
// Quick hack - texts should have LF if followed by blocks | ||
if (tokens[i + 1].type !== 'list_item_close') { | ||
result += '\n'; | ||
} | ||
continue; | ||
} | ||
if (tokens[i].type === 'inline') { | ||
result += this.renderInline(tokens[i].children, options); | ||
} else { | ||
result += rules[name](tokens, i, options); | ||
result += rules[tokens[i].type](tokens, i, options); | ||
} | ||
@@ -292,0 +255,0 @@ } |
@@ -135,3 +135,3 @@ // Ruler is helper class to build responsibility chains from parse rules. | ||
} | ||
this.rules.splice(index + 1, 0, fn); | ||
this.rules.splice(index + 1, 0, rule); | ||
} | ||
@@ -138,0 +138,0 @@ |
@@ -91,3 +91,5 @@ // Lists | ||
itemLines, | ||
terminatorRules = state.parser._rulesListTerm, i, l, terminate; | ||
tight = true, | ||
terminatorRules = state.parser._rulesListTerm, | ||
i, l, terminate, level, tokens; | ||
@@ -121,3 +123,2 @@ // Detect list type and position after marker | ||
order: markerValue, | ||
tight: true, | ||
lines: listLines = [ startLine, 0 ], | ||
@@ -130,3 +131,2 @@ level: state.level++ | ||
type: 'bullet_list_open', | ||
tight: true, | ||
lines: listLines = [ startLine, 0 ], | ||
@@ -189,3 +189,3 @@ level: state.level++ | ||
if (!state.tight || prevEmptyEnd) { | ||
state.tokens[listTokIdx].tight = false; | ||
tight = false; | ||
} | ||
@@ -201,3 +201,6 @@ // Item become loose if finish with empty line, | ||
state.tokens.push({ type: 'list_item_close', level: --state.level }); | ||
state.tokens.push({ | ||
type: 'list_item_close', | ||
level: --state.level | ||
}); | ||
@@ -242,11 +245,25 @@ nextLine = startLine = state.line; | ||
// Finilize list | ||
if (isOrdered) { | ||
state.tokens.push({ type: 'ordered_list_close', level: --state.level }); | ||
} else { | ||
state.tokens.push({ type: 'bullet_list_close', level: --state.level }); | ||
} | ||
state.tokens.push({ | ||
type: isOrdered ? 'ordered_list_close' : 'bullet_list_close', | ||
level: --state.level | ||
}); | ||
listLines[1] = nextLine; | ||
state.line = nextLine; | ||
// mark paragraphs tight if needed | ||
if (tight) { | ||
level = state.level + 2; | ||
tokens = state.tokens; | ||
for (i = listTokIdx + 2, l = tokens.length - 2; i < l; i++) { | ||
if (tokens[i].level === level && tokens[i].type === 'paragraph_open') { | ||
tokens[i].tight = true; | ||
i += 2; | ||
tokens[i].tight = true; | ||
} | ||
} | ||
} | ||
return true; | ||
}; |
@@ -45,2 +45,3 @@ // Paragraph | ||
type: 'paragraph_open', | ||
tight: false, | ||
lines: [ startLine, state.line ], | ||
@@ -58,2 +59,3 @@ level: state.level | ||
type: 'paragraph_close', | ||
tight: false, | ||
level: state.level | ||
@@ -60,0 +62,0 @@ }); |
@@ -13,3 +13,3 @@ // Process autolinks '<protocol:...>' | ||
module.exports = function autolink(state) { | ||
module.exports = function autolink(state, silent) { | ||
var tail, linkMatch, emailMatch, url, pos = state.pos; | ||
@@ -32,13 +32,15 @@ | ||
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 }); | ||
} | ||
@@ -57,13 +59,15 @@ state.pos += linkMatch[0].length; | ||
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 }); | ||
} | ||
@@ -70,0 +74,0 @@ state.pos += emailMatch[0].length; |
// Parse backticks | ||
module.exports = function backticks(state) { | ||
module.exports = function backticks(state, silent) { | ||
var start, max, marker, matchStart, matchEnd, | ||
@@ -26,10 +26,12 @@ pos = state.pos, | ||
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; | ||
@@ -40,5 +42,5 @@ return true; | ||
state.pending += marker; | ||
if (!silent) { state.pending += marker; } | ||
state.pos += marker.length; | ||
return true; | ||
}; |
@@ -12,9 +12,8 @@ // Process *this* and _that_ | ||
// returns the amount of markers (1, 2, 3, 4+), or -1 on failure; | ||
// parse sequence of emphasis markers, | ||
// "start" should point at a valid marker | ||
// | ||
// note: in case if 4+ markers it is still not a valid emphasis, | ||
// should be treated as a special case | ||
function parseStart(state, start) { | ||
function scanDelims(state, start) { | ||
var pos = start, lastChar, nextChar, count, | ||
can_open = true, | ||
can_close = true, | ||
max = state.posMax, | ||
@@ -26,85 +25,39 @@ marker = state.src.charCodeAt(start); | ||
while (pos < max && state.src.charCodeAt(pos) === marker) { pos++; } | ||
if (pos >= max) { return -1; } | ||
if (pos >= max) { can_open = false; } | ||
count = pos - start; | ||
// Quoting spec: | ||
// | ||
// Character can open emphasis iff | ||
// 1. it is not part of a sequence of four or more unescaped markers, | ||
// 2. it is not followed by whitespace, | ||
// 3. it is "_" and it is not preceded by an ASCII alphanumeric character, and | ||
// 4. either it is not followed by a marker or it is followed immediately by strong emphasis. | ||
if (count >= 4) { | ||
// check condition 1 | ||
// sequence of four or more unescaped markers can't start an emphasis | ||
return count; | ||
} | ||
// sequence of four or more unescaped markers can't start/end an emphasis | ||
can_open = can_close = false; | ||
} else { | ||
nextChar = pos < max ? state.src.charCodeAt(pos) : -1; | ||
// check condition 2, marker followed by whitespace | ||
nextChar = state.src.charCodeAt(pos); | ||
if (nextChar === 0x20 || nextChar === 0x0A) { return -1; } | ||
// check whitespace conditions | ||
if (nextChar === 0x20 || nextChar === 0x0A) { can_open = false; } | ||
if (lastChar === 0x20 || lastChar === 0x0A) { can_close = false; } | ||
if (marker === 0x5F /* _ */) { | ||
// check condition 3, if it's the beginning of the word | ||
// we need to look back for this | ||
if (isAlphaNum(lastChar)) { return -1; } | ||
if (marker === 0x5F /* _ */) { | ||
// check if we aren't inside the word | ||
if (isAlphaNum(lastChar)) { can_open = false; } | ||
if (isAlphaNum(nextChar)) { can_close = false; } | ||
} | ||
} | ||
return count; | ||
return { | ||
can_open: can_open, | ||
can_close: can_close, | ||
delims: count | ||
}; | ||
} | ||
// returns the amount of markers (1, 2, 3, 4+), or -1 on failure; | ||
// "start" should point at a valid marker | ||
// | ||
// note: in case if 4+ markers it is still not a valid emphasis, | ||
// should be treated as a special case | ||
function parseEnd(state, start) { | ||
var pos = start, lastChar, count, | ||
max = state.posMax, | ||
marker = state.src.charCodeAt(start); | ||
lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; | ||
while (pos < max && state.src.charCodeAt(pos) === marker) { pos++; } | ||
count = pos - start; | ||
// Quoting spec: | ||
// | ||
// Character can close emphasis iff | ||
// 1. it is not part of a sequence of four or more unescaped markers, | ||
// 2. it is not preceded by whitespace, | ||
// 3. it is not "_" or it is not followed by an ASCII alphanumeric character | ||
if (count >= 4) { | ||
// check condition 1 | ||
// sequence of four or more unescaped markers can't start an emphasis | ||
return count; | ||
} | ||
// check condition 2, marker preceded by whitespace | ||
if (lastChar === 0x20 || lastChar === 0x0A) { return -1; } | ||
if (marker === 0x5F) { | ||
// check condition 3, if it's the end of the word | ||
if (pos < max && isAlphaNum(state.src.charCodeAt(pos))) { return -1; } | ||
} | ||
return count; | ||
} | ||
module.exports = function emphasis(state/*, silent*/) { | ||
module.exports = function emphasis(state, silent) { | ||
var startCount, | ||
count, | ||
oldLength, | ||
oldPending, | ||
oldFlag, | ||
found, | ||
ok, | ||
oldCount, | ||
newCount, | ||
stack, | ||
res, | ||
max = state.posMax, | ||
start = state.pos, | ||
haveLiteralAsterisk, | ||
marker = state.src.charCodeAt(start); | ||
@@ -117,9 +70,9 @@ | ||
// [foo `bar]()` | ||
if (state.validateInsideEm || state.validateInsideLink) { return false; } | ||
if (silent && state.isInLabel) { return false; } | ||
startCount = parseStart(state, start); | ||
if (startCount < 0) { return false; } | ||
if (startCount >= 4) { | ||
res = scanDelims(state, start); | ||
startCount = res.delims; | ||
if (!res.can_open) { | ||
state.pos += startCount; | ||
state.pending += state.src.slice(start, startCount); | ||
if (!silent) { state.pending += state.src.slice(start, state.pos); } | ||
return true; | ||
@@ -130,14 +83,10 @@ } | ||
oldLength = state.tokens.length; | ||
oldPending = state.pending; | ||
oldFlag = state.validateInsideEm; | ||
state.pos = start + startCount; | ||
stack = [ startCount ]; | ||
state.validateInsideEm = true; | ||
while (state.pos < max) { | ||
if (state.src.charCodeAt(state.pos) === marker && !haveLiteralAsterisk) { | ||
count = parseEnd(state, state.pos); | ||
if (count >= 1 && count < 4) { | ||
if (state.src.charCodeAt(state.pos) === marker) { | ||
res = scanDelims(state, state.pos); | ||
count = res.delims; | ||
if (res.can_close) { | ||
oldCount = stack.pop(); | ||
@@ -168,27 +117,7 @@ newCount = count; | ||
} | ||
count = parseStart(state, state.pos); | ||
if (count >= 1 && count < 4) { | ||
stack.push(count); | ||
state.pos += count; | ||
continue; | ||
} | ||
} | ||
ok = state.parser.tokenizeSingle(state); | ||
if (ok) { | ||
haveLiteralAsterisk = false; | ||
} else { | ||
haveLiteralAsterisk = state.src.charCodeAt(state.pos) === marker; | ||
state.pending += state.src[state.pos]; | ||
state.pos++; | ||
} | ||
if (!state.parser.skipToken(state)) { state.pos++; } | ||
} | ||
// restore old state | ||
state.tokens.length = oldLength; | ||
state.pending = oldPending; | ||
state.validateInsideEm = oldFlag; | ||
if (!found) { | ||
@@ -204,17 +133,19 @@ // parser failed to find ending tag, so it's not valid emphasis | ||
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 (!silent) { | ||
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 === 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 === 2 || startCount === 3) { | ||
state.push({ type: 'strong_close', level: --state.level }); | ||
} | ||
@@ -221,0 +152,0 @@ state.pos = state.posMax + startCount; |
@@ -15,3 +15,3 @@ // Proceess html entity - {, ¯, ", ... | ||
module.exports = function entity(state) { | ||
module.exports = function entity(state, silent) { | ||
var ch, code, match, pos = state.pos, max = state.posMax; | ||
@@ -27,4 +27,6 @@ | ||
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; | ||
@@ -37,3 +39,3 @@ return true; | ||
if (entities.hasOwnProperty(match[1])) { | ||
state.pending += escapeHtml(entities[match[1]]); | ||
if (!silent) { state.pending += escapeHtml(entities[match[1]]); } | ||
state.pos += match[0].length; | ||
@@ -46,5 +48,5 @@ return true; | ||
state.pending += '&'; | ||
if (!silent) { state.pending += '&'; } | ||
state.pos++; | ||
return true; | ||
}; |
// 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 += '<'; | ||
str = '<'; | ||
} else if (ch === 0x3E/* > */) { | ||
state.pending += '>'; | ||
str = '>'; | ||
} else if (ch === 0x22/* " */) { | ||
state.pending += '"'; | ||
str = '"'; | ||
} else { | ||
@@ -16,4 +17,5 @@ return false; | ||
if (!silent) { state.pending += str; } | ||
state.pos++; | ||
return true; | ||
}; |
@@ -8,4 +8,4 @@ // Proceess escaped chars and hardbreaks | ||
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; | ||
@@ -22,12 +22,13 @@ if (state.src.charCodeAt(pos) !== 0x5C/* \ */) { return false; } | ||
if (ch === 0x26/* & */) { | ||
state.pending += '&'; | ||
str = '&'; | ||
} else if (ch === 0x3C/* < */) { | ||
state.pending += '<'; | ||
str = '<'; | ||
} else if (ch === 0x3E/* > */) { | ||
state.pending += '>'; | ||
str = '>'; | ||
} else if (ch === 0x22/* " */) { | ||
state.pending += '"'; | ||
str = '"'; | ||
} else { | ||
state.pending += state.src[pos]; | ||
str = state.src[pos]; | ||
} | ||
if (!silent) { state.pending += str; } | ||
state.pos += 2; | ||
@@ -38,6 +39,8 @@ return true; | ||
if (ch === 0x0A) { | ||
state.push({ | ||
type: 'hardbreak', | ||
level: state.level | ||
}); | ||
if (!silent) { | ||
state.push({ | ||
type: 'hardbreak', | ||
level: state.level | ||
}); | ||
} | ||
@@ -53,5 +56,5 @@ pos++; | ||
state.pending += '\\'; | ||
if (!silent) { state.pending += '\\'; } | ||
state.pos++; | ||
return true; | ||
}; |
@@ -16,3 +16,3 @@ // Process html tags | ||
module.exports = function htmltag(state) { | ||
module.exports = function htmltag(state, silent) { | ||
var ch, match, max, pos = state.pos; | ||
@@ -41,10 +41,11 @@ | ||
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; | ||
}; |
@@ -11,3 +11,3 @@ // Process [links](<to> "stuff") | ||
function links(state) { | ||
module.exports = function links(state, silent) { | ||
var labelStart, | ||
@@ -59,3 +59,3 @@ labelEnd, | ||
if (parseLinkDestination(state, pos)) { | ||
href = state.link_content; | ||
href = state.linkContent; | ||
pos = state.pos; | ||
@@ -77,3 +77,3 @@ } else { | ||
if (pos < max && start !== pos && parseLinkTitle(state, pos)) { | ||
title = state.link_content; | ||
title = state.linkContent; | ||
pos = state.pos; | ||
@@ -138,24 +138,26 @@ | ||
// | ||
state.pos = labelStart; | ||
state.posMax = labelEnd; | ||
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 }); | ||
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 }); | ||
} | ||
} | ||
@@ -166,4 +168,2 @@ | ||
return true; | ||
} | ||
module.exports = links; | ||
}; |
// Proceess '\n' | ||
module.exports = function newline(state) { | ||
module.exports = function newline(state, silent) { | ||
var pmax, max, pos = state.pos; | ||
@@ -15,11 +15,19 @@ | ||
// 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({ | ||
@@ -30,8 +38,2 @@ type: 'softbreak', | ||
} | ||
} else { | ||
state.push({ | ||
type: 'softbreak', | ||
level: state.level | ||
}); | ||
} | ||
@@ -38,0 +40,0 @@ |
@@ -18,7 +18,18 @@ // Inline parser state | ||
this.validateInsideEm = false; | ||
this.validateInsideLink = false; | ||
this.linkLevel = 0; | ||
this.link_content = ''; | ||
this.label_nest_level = 0; // for stmd-like backtrack optimization | ||
this.cache = {}; // Stores { start: end } pairs. Useful for backtrack | ||
// optimization of pairs parse (emphasis, strikes). | ||
// Link parser state vars | ||
this.isInLabel = false; // Set true when seek link label - we should disable | ||
// "paired" rules (emphasis, strikes) to not skip | ||
// tailing `]` | ||
this.linkLevel = 0; // Increment for each nesting link. Used to prevent | ||
// nesting in definitions | ||
this.linkContent = ''; // Temporary storage for link url | ||
this.labelUnmatchedScopes = 0; // Track unpaired `[` for link labels | ||
// (backtrack optimization) | ||
} | ||
@@ -25,0 +36,0 @@ |
@@ -5,10 +5,5 @@ // Process ~~strike through~~ | ||
module.exports = function strikethrough(state) { | ||
var oldLength, | ||
oldPending, | ||
oldFlag, | ||
found, | ||
ok, | ||
module.exports = function strikethrough(state, silent) { | ||
var found, | ||
pos, | ||
stack, | ||
max = state.posMax, | ||
@@ -25,3 +20,3 @@ start = state.pos, | ||
// this code also prevents recursion | ||
if (state.validateInsideEm || state.validateInsideLink) { return false; } | ||
if (silent && state.isInLabel) { return false; } | ||
@@ -42,13 +37,7 @@ if (state.level >= state.options.maxNesting) { return false; } | ||
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; | ||
state.validateInsideEm = true; | ||
stack = 1; | ||
@@ -63,10 +52,2 @@ while (state.pos + 1 < max) { | ||
// closing '~~' | ||
stack--; | ||
} else if (nextChar !== 0x20 && nextChar !== 0x0A) { | ||
// opening '~~' | ||
stack++; | ||
} // else { | ||
// // standalone ' ~~ ' indented with spaces | ||
//} | ||
if (stack <= 0) { | ||
found = true; | ||
@@ -79,15 +60,5 @@ break; | ||
ok = state.parser.tokenizeSingle(state); | ||
if (!ok) { | ||
state.pending += state.src[state.pos]; | ||
state.pos++; | ||
} | ||
if (!state.parser.skipToken(state)) { state.pos++; } | ||
} | ||
// restore old state | ||
state.tokens.length = oldLength; | ||
state.pending = oldPending; | ||
state.validateInsideEm = oldFlag; | ||
if (!found) { | ||
@@ -103,5 +74,7 @@ // parser failed to find ending tag, so it's not valid emphasis | ||
state.push({ type: 'del_open', level: state.level++ }); | ||
state.parser.tokenize(state); | ||
state.push({ type: 'del_close', level: --state.level }); | ||
if (!silent) { | ||
state.push({ type: 'del_open', level: state.level++ }); | ||
state.parser.tokenize(state); | ||
state.push({ type: 'del_close', level: --state.level }); | ||
} | ||
@@ -108,0 +81,0 @@ state.pos = state.posMax + 2; |
// 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); | ||
@@ -9,3 +9,3 @@ | ||
state.pending += match[0]; | ||
if (!silent) { state.pending += match[0]; } | ||
state.pos += match[0].length; | ||
@@ -12,0 +12,0 @@ |
@@ -18,3 +18,3 @@ // Replace link-like texts with link nodes. | ||
if (match.getType() === 'url') { | ||
links.push(match.getUrl()); | ||
links.push({ text: match.matchedText, url: match.getUrl() }); | ||
} | ||
@@ -79,5 +79,8 @@ return false; | ||
if (!state.parser.validateLink(links[ln])) { continue; } | ||
if (!state.parser.validateLink(links[ln].url)) { continue; } | ||
pos = text.indexOf(links[ln]); | ||
pos = text.indexOf(links[ln].text); | ||
if (pos === -1) { continue; } | ||
if (pos) { | ||
@@ -93,3 +96,3 @@ level = level; | ||
type: 'link_open', | ||
href: links[ln], | ||
href: links[ln].url, | ||
title: '', | ||
@@ -100,3 +103,3 @@ level: level++ | ||
type: 'text', | ||
content: escapeHtml(links[ln]), | ||
content: escapeHtml(links[ln].text), | ||
level: level | ||
@@ -108,3 +111,3 @@ }); | ||
}); | ||
text = text.slice(pos + links[ln].length); | ||
text = text.slice(pos + links[ln].text.length); | ||
} | ||
@@ -111,0 +114,0 @@ if (text.length) { |
{ | ||
"name": "remarkable", | ||
"version": "1.1.0", | ||
"version": "1.1.1", | ||
"description": "Markdown parser, done right. Commonmark support, extensions, syntax plugins, high speed - all in one.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
441569
-1.14%12311
-1.13%