diff options
Diffstat (limited to 'node_modules/markdown-it/lib/rules_block')
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/blockquote.js | 284 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/code.js | 34 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/fence.js | 98 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/heading.js | 55 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/hr.js | 45 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/html_block.js | 74 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/lheading.js | 83 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/list.js | 364 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/paragraph.js | 52 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/reference.js | 198 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/state_block.js | 231 | ||||
-rw-r--r-- | node_modules/markdown-it/lib/rules_block/table.js | 221 |
12 files changed, 1739 insertions, 0 deletions
diff --git a/node_modules/markdown-it/lib/rules_block/blockquote.js b/node_modules/markdown-it/lib/rules_block/blockquote.js new file mode 100644 index 0000000..a02699a --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/blockquote.js @@ -0,0 +1,284 @@ +// Block quotes + +'use strict'; + +var isSpace = require('../common/utils').isSpace; + + +module.exports = function blockquote(state, startLine, endLine, silent) { + var adjustTab, + ch, + i, + initial, + l, + lastLineEmpty, + lines, + nextLine, + offset, + oldBMarks, + oldBSCount, + oldIndent, + oldParentType, + oldSCount, + oldTShift, + spaceAfterMarker, + terminate, + terminatorRules, + token, + isOutdented, + oldLineMax = state.lineMax, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } + + // check the block quote marker + if (state.src.charCodeAt(pos++) !== 0x3E/* > */) { return false; } + + // we know that it's going to be a valid blockquote, + // so no point trying to find the end of it in silent mode + if (silent) { return true; } + + // set offset past spaces and ">" + initial = offset = state.sCount[startLine] + 1; + + // skip one optional space after '>' + if (state.src.charCodeAt(pos) === 0x20 /* space */) { + // ' > test ' + // ^ -- position start of line here: + pos++; + initial++; + offset++; + adjustTab = false; + spaceAfterMarker = true; + } else if (state.src.charCodeAt(pos) === 0x09 /* tab */) { + spaceAfterMarker = true; + + if ((state.bsCount[startLine] + offset) % 4 === 3) { + // ' >\t test ' + // ^ -- position start of line here (tab has width===1) + pos++; + initial++; + offset++; + adjustTab = false; + } else { + // ' >\t test ' + // ^ -- position start of line here + shift bsCount slightly + // to make extra space appear + adjustTab = true; + } + } else { + spaceAfterMarker = false; + } + + oldBMarks = [ state.bMarks[startLine] ]; + state.bMarks[startLine] = pos; + + while (pos < max) { + ch = state.src.charCodeAt(pos); + + if (isSpace(ch)) { + if (ch === 0x09) { + offset += 4 - (offset + state.bsCount[startLine] + (adjustTab ? 1 : 0)) % 4; + } else { + offset++; + } + } else { + break; + } + + pos++; + } + + oldBSCount = [ state.bsCount[startLine] ]; + state.bsCount[startLine] = state.sCount[startLine] + 1 + (spaceAfterMarker ? 1 : 0); + + lastLineEmpty = pos >= max; + + oldSCount = [ state.sCount[startLine] ]; + state.sCount[startLine] = offset - initial; + + oldTShift = [ state.tShift[startLine] ]; + state.tShift[startLine] = pos - state.bMarks[startLine]; + + terminatorRules = state.md.block.ruler.getRules('blockquote'); + + oldParentType = state.parentType; + state.parentType = 'blockquote'; + + // Search the end of the block + // + // Block ends with either: + // 1. an empty line outside: + // ``` + // > test + // + // ``` + // 2. an empty line inside: + // ``` + // > + // test + // ``` + // 3. another tag: + // ``` + // > test + // - - - + // ``` + for (nextLine = startLine + 1; nextLine < endLine; nextLine++) { + // check if it's outdented, i.e. it's inside list item and indented + // less than said list item: + // + // ``` + // 1. anything + // > current blockquote + // 2. checking this line + // ``` + isOutdented = state.sCount[nextLine] < state.blkIndent; + + pos = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + + if (pos >= max) { + // Case 1: line is not inside the blockquote, and this line is empty. + break; + } + + if (state.src.charCodeAt(pos++) === 0x3E/* > */ && !isOutdented) { + // This line is inside the blockquote. + + // set offset past spaces and ">" + initial = offset = state.sCount[nextLine] + 1; + + // skip one optional space after '>' + if (state.src.charCodeAt(pos) === 0x20 /* space */) { + // ' > test ' + // ^ -- position start of line here: + pos++; + initial++; + offset++; + adjustTab = false; + spaceAfterMarker = true; + } else if (state.src.charCodeAt(pos) === 0x09 /* tab */) { + spaceAfterMarker = true; + + if ((state.bsCount[nextLine] + offset) % 4 === 3) { + // ' >\t test ' + // ^ -- position start of line here (tab has width===1) + pos++; + initial++; + offset++; + adjustTab = false; + } else { + // ' >\t test ' + // ^ -- position start of line here + shift bsCount slightly + // to make extra space appear + adjustTab = true; + } + } else { + spaceAfterMarker = false; + } + + oldBMarks.push(state.bMarks[nextLine]); + state.bMarks[nextLine] = pos; + + while (pos < max) { + ch = state.src.charCodeAt(pos); + + if (isSpace(ch)) { + if (ch === 0x09) { + offset += 4 - (offset + state.bsCount[nextLine] + (adjustTab ? 1 : 0)) % 4; + } else { + offset++; + } + } else { + break; + } + + pos++; + } + + lastLineEmpty = pos >= max; + + oldBSCount.push(state.bsCount[nextLine]); + state.bsCount[nextLine] = state.sCount[nextLine] + 1 + (spaceAfterMarker ? 1 : 0); + + oldSCount.push(state.sCount[nextLine]); + state.sCount[nextLine] = offset - initial; + + oldTShift.push(state.tShift[nextLine]); + state.tShift[nextLine] = pos - state.bMarks[nextLine]; + continue; + } + + // Case 2: line is not inside the blockquote, and the last line was empty. + if (lastLineEmpty) { break; } + + // Case 3: another tag found. + terminate = false; + for (i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + + if (terminate) { + // Quirk to enforce "hard termination mode" for paragraphs; + // normally if you call `tokenize(state, startLine, nextLine)`, + // paragraphs will look below nextLine for paragraph continuation, + // but if blockquote is terminated by another tag, they shouldn't + state.lineMax = nextLine; + + if (state.blkIndent !== 0) { + // state.blkIndent was non-zero, we now set it to zero, + // so we need to re-calculate all offsets to appear as + // if indent wasn't changed + oldBMarks.push(state.bMarks[nextLine]); + oldBSCount.push(state.bsCount[nextLine]); + oldTShift.push(state.tShift[nextLine]); + oldSCount.push(state.sCount[nextLine]); + state.sCount[nextLine] -= state.blkIndent; + } + + break; + } + + oldBMarks.push(state.bMarks[nextLine]); + oldBSCount.push(state.bsCount[nextLine]); + oldTShift.push(state.tShift[nextLine]); + oldSCount.push(state.sCount[nextLine]); + + // A negative indentation means that this is a paragraph continuation + // + state.sCount[nextLine] = -1; + } + + oldIndent = state.blkIndent; + state.blkIndent = 0; + + token = state.push('blockquote_open', 'blockquote', 1); + token.markup = '>'; + token.map = lines = [ startLine, 0 ]; + + state.md.block.tokenize(state, startLine, nextLine); + + token = state.push('blockquote_close', 'blockquote', -1); + token.markup = '>'; + + state.lineMax = oldLineMax; + state.parentType = oldParentType; + lines[1] = state.line; + + // Restore original tShift; this might not be necessary since the parser + // has already been here, but just to make sure we can do that. + for (i = 0; i < oldTShift.length; i++) { + state.bMarks[i + startLine] = oldBMarks[i]; + state.tShift[i + startLine] = oldTShift[i]; + state.sCount[i + startLine] = oldSCount[i]; + state.bsCount[i + startLine] = oldBSCount[i]; + } + state.blkIndent = oldIndent; + + return true; +}; diff --git a/node_modules/markdown-it/lib/rules_block/code.js b/node_modules/markdown-it/lib/rules_block/code.js new file mode 100644 index 0000000..018e019 --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/code.js @@ -0,0 +1,34 @@ +// Code block (4 spaces padded) + +'use strict'; + + +module.exports = function code(state, startLine, endLine/*, silent*/) { + var nextLine, last, token; + + if (state.sCount[startLine] - state.blkIndent < 4) { return false; } + + last = nextLine = startLine + 1; + + while (nextLine < endLine) { + if (state.isEmpty(nextLine)) { + nextLine++; + continue; + } + + if (state.sCount[nextLine] - state.blkIndent >= 4) { + nextLine++; + last = nextLine; + continue; + } + break; + } + + state.line = last; + + token = state.push('code_block', 'code', 0); + token.content = state.getLines(startLine, last, 4 + state.blkIndent, false) + '\n'; + token.map = [ startLine, state.line ]; + + return true; +}; diff --git a/node_modules/markdown-it/lib/rules_block/fence.js b/node_modules/markdown-it/lib/rules_block/fence.js new file mode 100644 index 0000000..44f1538 --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/fence.js @@ -0,0 +1,98 @@ +// fences (``` lang, ~~~ lang) + +'use strict'; + + +module.exports = function fence(state, startLine, endLine, silent) { + var marker, len, params, nextLine, mem, token, markup, + haveEndMarker = false, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } + + if (pos + 3 > max) { return false; } + + marker = state.src.charCodeAt(pos); + + if (marker !== 0x7E/* ~ */ && marker !== 0x60 /* ` */) { + return false; + } + + // scan marker length + mem = pos; + pos = state.skipChars(pos, marker); + + len = pos - mem; + + if (len < 3) { return false; } + + markup = state.src.slice(mem, pos); + params = state.src.slice(pos, max); + + if (marker === 0x60 /* ` */) { + if (params.indexOf(String.fromCharCode(marker)) >= 0) { + return false; + } + } + + // Since start is found, we can report success here in validation mode + if (silent) { return true; } + + // search end of block + nextLine = startLine; + + for (;;) { + nextLine++; + if (nextLine >= endLine) { + // unclosed block should be autoclosed by end of document. + // also block seems to be autoclosed by end of parent + break; + } + + pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + + if (pos < max && state.sCount[nextLine] < state.blkIndent) { + // non-empty line with negative indent should stop the list: + // - ``` + // test + break; + } + + if (state.src.charCodeAt(pos) !== marker) { continue; } + + if (state.sCount[nextLine] - state.blkIndent >= 4) { + // closing fence should be indented less than 4 spaces + continue; + } + + pos = state.skipChars(pos, marker); + + // closing code fence must be at least as long as the opening one + if (pos - mem < len) { continue; } + + // make sure tail has spaces only + pos = state.skipSpaces(pos); + + if (pos < max) { continue; } + + haveEndMarker = true; + // found! + break; + } + + // If a fence has heading spaces, they should be removed from its inner block + len = state.sCount[startLine]; + + state.line = nextLine + (haveEndMarker ? 1 : 0); + + token = state.push('fence', 'code', 0); + token.info = params; + token.content = state.getLines(startLine + 1, nextLine, len, true); + token.markup = markup; + token.map = [ startLine, state.line ]; + + return true; +}; diff --git a/node_modules/markdown-it/lib/rules_block/heading.js b/node_modules/markdown-it/lib/rules_block/heading.js new file mode 100644 index 0000000..9863f48 --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/heading.js @@ -0,0 +1,55 @@ +// heading (#, ##, ...) + +'use strict'; + +var isSpace = require('../common/utils').isSpace; + + +module.exports = function heading(state, startLine, endLine, silent) { + var ch, level, tmp, token, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } + + ch = state.src.charCodeAt(pos); + + if (ch !== 0x23/* # */ || pos >= max) { return false; } + + // count heading level + level = 1; + ch = state.src.charCodeAt(++pos); + while (ch === 0x23/* # */ && pos < max && level <= 6) { + level++; + ch = state.src.charCodeAt(++pos); + } + + if (level > 6 || (pos < max && !isSpace(ch))) { return false; } + + if (silent) { return true; } + + // Let's cut tails like ' ### ' from the end of string + + max = state.skipSpacesBack(max, pos); + tmp = state.skipCharsBack(max, 0x23, pos); // # + if (tmp > pos && isSpace(state.src.charCodeAt(tmp - 1))) { + max = tmp; + } + + state.line = startLine + 1; + + token = state.push('heading_open', 'h' + String(level), 1); + token.markup = '########'.slice(0, level); + token.map = [ startLine, state.line ]; + + token = state.push('inline', '', 0); + token.content = state.src.slice(pos, max).trim(); + token.map = [ startLine, state.line ]; + token.children = []; + + token = state.push('heading_close', 'h' + String(level), -1); + token.markup = '########'.slice(0, level); + + return true; +}; diff --git a/node_modules/markdown-it/lib/rules_block/hr.js b/node_modules/markdown-it/lib/rules_block/hr.js new file mode 100644 index 0000000..a3bb14e --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/hr.js @@ -0,0 +1,45 @@ +// Horizontal rule + +'use strict'; + +var isSpace = require('../common/utils').isSpace; + + +module.exports = function hr(state, startLine, endLine, silent) { + var marker, cnt, ch, token, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } + + marker = state.src.charCodeAt(pos++); + + // Check hr marker + if (marker !== 0x2A/* * */ && + marker !== 0x2D/* - */ && + marker !== 0x5F/* _ */) { + return false; + } + + // markers can be mixed with spaces, but there should be at least 3 of them + + cnt = 1; + while (pos < max) { + ch = state.src.charCodeAt(pos++); + if (ch !== marker && !isSpace(ch)) { return false; } + if (ch === marker) { cnt++; } + } + + if (cnt < 3) { return false; } + + if (silent) { return true; } + + state.line = startLine + 1; + + token = state.push('hr', 'hr', 0); + token.map = [ startLine, state.line ]; + token.markup = Array(cnt + 1).join(String.fromCharCode(marker)); + + return true; +}; diff --git a/node_modules/markdown-it/lib/rules_block/html_block.js b/node_modules/markdown-it/lib/rules_block/html_block.js new file mode 100644 index 0000000..2f17675 --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/html_block.js @@ -0,0 +1,74 @@ +// HTML block + +'use strict'; + + +var block_names = require('../common/html_blocks'); +var HTML_OPEN_CLOSE_TAG_RE = require('../common/html_re').HTML_OPEN_CLOSE_TAG_RE; + +// An array of opening and corresponding closing sequences for html tags, +// last argument defines whether it can terminate a paragraph or not +// +var HTML_SEQUENCES = [ + [ /^<(script|pre|style|textarea)(?=(\s|>|$))/i, /<\/(script|pre|style|textarea)>/i, true ], + [ /^<!--/, /-->/, true ], + [ /^<\?/, /\?>/, true ], + [ /^<![A-Z]/, />/, true ], + [ /^<!\[CDATA\[/, /\]\]>/, true ], + [ new RegExp('^</?(' + block_names.join('|') + ')(?=(\\s|/?>|$))', 'i'), /^$/, true ], + [ new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\s*$'), /^$/, false ] +]; + + +module.exports = function html_block(state, startLine, endLine, silent) { + var i, nextLine, token, lineText, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } + + if (!state.md.options.html) { return false; } + + if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; } + + lineText = state.src.slice(pos, max); + + for (i = 0; i < HTML_SEQUENCES.length; i++) { + if (HTML_SEQUENCES[i][0].test(lineText)) { break; } + } + + if (i === HTML_SEQUENCES.length) { return false; } + + if (silent) { + // true if this sequence can be a terminator, false otherwise + return HTML_SEQUENCES[i][2]; + } + + nextLine = startLine + 1; + + // If we are here - we detected HTML block. + // Let's roll down till block end. + if (!HTML_SEQUENCES[i][1].test(lineText)) { + for (; nextLine < endLine; nextLine++) { + if (state.sCount[nextLine] < state.blkIndent) { break; } + + pos = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + lineText = state.src.slice(pos, max); + + if (HTML_SEQUENCES[i][1].test(lineText)) { + if (lineText.length !== 0) { nextLine++; } + break; + } + } + } + + state.line = nextLine; + + token = state.push('html_block', '', 0); + token.map = [ startLine, nextLine ]; + token.content = state.getLines(startLine, nextLine, state.blkIndent, true); + + return true; +}; diff --git a/node_modules/markdown-it/lib/rules_block/lheading.js b/node_modules/markdown-it/lib/rules_block/lheading.js new file mode 100644 index 0000000..19bdc39 --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/lheading.js @@ -0,0 +1,83 @@ +// lheading (---, ===) + +'use strict'; + + +module.exports = function lheading(state, startLine, endLine/*, silent*/) { + var content, terminate, i, l, token, pos, max, level, marker, + nextLine = startLine + 1, oldParentType, + terminatorRules = state.md.block.ruler.getRules('paragraph'); + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } + + oldParentType = state.parentType; + state.parentType = 'paragraph'; // use paragraph to match terminatorRules + + // jump line-by-line until empty one or EOF + for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { + // this would be a code block normally, but after paragraph + // it's considered a lazy continuation regardless of what's there + if (state.sCount[nextLine] - state.blkIndent > 3) { continue; } + + // + // Check for underline in setext header + // + if (state.sCount[nextLine] >= state.blkIndent) { + pos = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + + if (pos < max) { + marker = state.src.charCodeAt(pos); + + if (marker === 0x2D/* - */ || marker === 0x3D/* = */) { + pos = state.skipChars(pos, marker); + pos = state.skipSpaces(pos); + + if (pos >= max) { + level = (marker === 0x3D/* = */ ? 1 : 2); + break; + } + } + } + } + + // quirk for blockquotes, this line should already be checked by that rule + if (state.sCount[nextLine] < 0) { continue; } + + // Some tags can terminate paragraph without empty line. + terminate = false; + for (i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + if (terminate) { break; } + } + + if (!level) { + // Didn't find valid underline + return false; + } + + content = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); + + state.line = nextLine + 1; + + token = state.push('heading_open', 'h' + String(level), 1); + token.markup = String.fromCharCode(marker); + token.map = [ startLine, state.line ]; + + token = state.push('inline', '', 0); + token.content = content; + token.map = [ startLine, state.line - 1 ]; + token.children = []; + + token = state.push('heading_close', 'h' + String(level), -1); + token.markup = String.fromCharCode(marker); + + state.parentType = oldParentType; + + return true; +}; diff --git a/node_modules/markdown-it/lib/rules_block/list.js b/node_modules/markdown-it/lib/rules_block/list.js new file mode 100644 index 0000000..1e5e87b --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/list.js @@ -0,0 +1,364 @@ +// Lists + +'use strict'; + +var isSpace = require('../common/utils').isSpace; + + +// Search `[-+*][\n ]`, returns next pos after marker on success +// or -1 on fail. +function skipBulletListMarker(state, startLine) { + var marker, pos, max, ch; + + pos = state.bMarks[startLine] + state.tShift[startLine]; + max = state.eMarks[startLine]; + + marker = state.src.charCodeAt(pos++); + // Check bullet + if (marker !== 0x2A/* * */ && + marker !== 0x2D/* - */ && + marker !== 0x2B/* + */) { + return -1; + } + + if (pos < max) { + ch = state.src.charCodeAt(pos); + + if (!isSpace(ch)) { + // " -test " - is not a list item + return -1; + } + } + + return pos; +} + +// Search `\d+[.)][\n ]`, returns next pos after marker on success +// or -1 on fail. +function skipOrderedListMarker(state, startLine) { + var ch, + start = state.bMarks[startLine] + state.tShift[startLine], + pos = start, + max = state.eMarks[startLine]; + + // List marker should have at least 2 chars (digit + dot) + if (pos + 1 >= max) { return -1; } + + ch = state.src.charCodeAt(pos++); + + if (ch < 0x30/* 0 */ || ch > 0x39/* 9 */) { return -1; } + + for (;;) { + // EOL -> fail + if (pos >= max) { return -1; } + + ch = state.src.charCodeAt(pos++); + + if (ch >= 0x30/* 0 */ && ch <= 0x39/* 9 */) { + + // List marker should have no more than 9 digits + // (prevents integer overflow in browsers) + if (pos - start >= 10) { return -1; } + + continue; + } + + // found valid marker + if (ch === 0x29/* ) */ || ch === 0x2e/* . */) { + break; + } + + return -1; + } + + + if (pos < max) { + ch = state.src.charCodeAt(pos); + + if (!isSpace(ch)) { + // " 1.test " - is not a list item + return -1; + } + } + return pos; +} + +function markTightParagraphs(state, idx) { + var i, l, + level = state.level + 2; + + for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) { + if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') { + state.tokens[i + 2].hidden = true; + state.tokens[i].hidden = true; + i += 2; + } + } +} + + +module.exports = function list(state, startLine, endLine, silent) { + var ch, + contentStart, + i, + indent, + indentAfterMarker, + initial, + isOrdered, + itemLines, + l, + listLines, + listTokIdx, + markerCharCode, + markerValue, + max, + nextLine, + offset, + oldListIndent, + oldParentType, + oldSCount, + oldTShift, + oldTight, + pos, + posAfterMarker, + prevEmptyEnd, + start, + terminate, + terminatorRules, + token, + isTerminatingParagraph = false, + tight = true; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } + + // Special case: + // - item 1 + // - item 2 + // - item 3 + // - item 4 + // - this one is a paragraph continuation + if (state.listIndent >= 0 && + state.sCount[startLine] - state.listIndent >= 4 && + state.sCount[startLine] < state.blkIndent) { + return false; + } + + // limit conditions when list can interrupt + // a paragraph (validation mode only) + if (silent && state.parentType === 'paragraph') { + // Next list item should still terminate previous list item; + // + // This code can fail if plugins use blkIndent as well as lists, + // but I hope the spec gets fixed long before that happens. + // + if (state.sCount[startLine] >= state.blkIndent) { + isTerminatingParagraph = true; + } + } + + // Detect list type and position after marker + if ((posAfterMarker = skipOrderedListMarker(state, startLine)) >= 0) { + isOrdered = true; + start = state.bMarks[startLine] + state.tShift[startLine]; + markerValue = Number(state.src.slice(start, posAfterMarker - 1)); + + // If we're starting a new ordered list right after + // a paragraph, it should start with 1. + if (isTerminatingParagraph && markerValue !== 1) return false; + + } else if ((posAfterMarker = skipBulletListMarker(state, startLine)) >= 0) { + isOrdered = false; + + } else { + return false; + } + + // If we're starting a new unordered list right after + // a paragraph, first line should not be empty. + if (isTerminatingParagraph) { + if (state.skipSpaces(posAfterMarker) >= state.eMarks[startLine]) return false; + } + + // We should terminate list on style change. Remember first one to compare. + markerCharCode = state.src.charCodeAt(posAfterMarker - 1); + + // For validation mode we can terminate immediately + if (silent) { return true; } + + // Start list + listTokIdx = state.tokens.length; + + if (isOrdered) { + token = state.push('ordered_list_open', 'ol', 1); + if (markerValue !== 1) { + token.attrs = [ [ 'start', markerValue ] ]; + } + + } else { + token = state.push('bullet_list_open', 'ul', 1); + } + + token.map = listLines = [ startLine, 0 ]; + token.markup = String.fromCharCode(markerCharCode); + + // + // Iterate list items + // + + nextLine = startLine; + prevEmptyEnd = false; + terminatorRules = state.md.block.ruler.getRules('list'); + + oldParentType = state.parentType; + state.parentType = 'list'; + + while (nextLine < endLine) { + pos = posAfterMarker; + max = state.eMarks[nextLine]; + + initial = offset = state.sCount[nextLine] + posAfterMarker - (state.bMarks[startLine] + state.tShift[startLine]); + + while (pos < max) { + ch = state.src.charCodeAt(pos); + + if (ch === 0x09) { + offset += 4 - (offset + state.bsCount[nextLine]) % 4; + } else if (ch === 0x20) { + offset++; + } else { + break; + } + + pos++; + } + + contentStart = pos; + + if (contentStart >= max) { + // trimming space in "- \n 3" case, indent is 1 here + indentAfterMarker = 1; + } else { + indentAfterMarker = offset - initial; + } + + // If we have more than 4 spaces, the indent is 1 + // (the rest is just indented code block) + if (indentAfterMarker > 4) { indentAfterMarker = 1; } + + // " - test" + // ^^^^^ - calculating total length of this thing + indent = initial + indentAfterMarker; + + // Run subparser & write tokens + token = state.push('list_item_open', 'li', 1); + token.markup = String.fromCharCode(markerCharCode); + token.map = itemLines = [ startLine, 0 ]; + if (isOrdered) { + token.info = state.src.slice(start, posAfterMarker - 1); + } + + // change current state, then restore it after parser subcall + oldTight = state.tight; + oldTShift = state.tShift[startLine]; + oldSCount = state.sCount[startLine]; + + // - example list + // ^ listIndent position will be here + // ^ blkIndent position will be here + // + oldListIndent = state.listIndent; + state.listIndent = state.blkIndent; + state.blkIndent = indent; + + state.tight = true; + state.tShift[startLine] = contentStart - state.bMarks[startLine]; + state.sCount[startLine] = offset; + + if (contentStart >= max && state.isEmpty(startLine + 1)) { + // workaround for this case + // (list item is empty, list terminates before "foo"): + // ~~~~~~~~ + // - + // + // foo + // ~~~~~~~~ + state.line = Math.min(state.line + 2, endLine); + } else { + state.md.block.tokenize(state, startLine, endLine, true); + } + + // If any of list item is tight, mark list as tight + if (!state.tight || prevEmptyEnd) { + tight = false; + } + // Item become loose if finish with empty line, + // but we should filter last element, because it means list finish + prevEmptyEnd = (state.line - startLine) > 1 && state.isEmpty(state.line - 1); + + state.blkIndent = state.listIndent; + state.listIndent = oldListIndent; + state.tShift[startLine] = oldTShift; + state.sCount[startLine] = oldSCount; + state.tight = oldTight; + + token = state.push('list_item_close', 'li', -1); + token.markup = String.fromCharCode(markerCharCode); + + nextLine = startLine = state.line; + itemLines[1] = nextLine; + contentStart = state.bMarks[startLine]; + + if (nextLine >= endLine) { break; } + + // + // Try to check if list is terminated or continued. + // + if (state.sCount[nextLine] < state.blkIndent) { break; } + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { break; } + + // fail if terminating block found + terminate = false; + for (i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + if (terminate) { break; } + + // fail if list has another type + if (isOrdered) { + posAfterMarker = skipOrderedListMarker(state, nextLine); + if (posAfterMarker < 0) { break; } + start = state.bMarks[nextLine] + state.tShift[nextLine]; + } else { + posAfterMarker = skipBulletListMarker(state, nextLine); + if (posAfterMarker < 0) { break; } + } + + if (markerCharCode !== state.src.charCodeAt(posAfterMarker - 1)) { break; } + } + + // Finalize list + if (isOrdered) { + token = state.push('ordered_list_close', 'ol', -1); + } else { + token = state.push('bullet_list_close', 'ul', -1); + } + token.markup = String.fromCharCode(markerCharCode); + + listLines[1] = nextLine; + state.line = nextLine; + + state.parentType = oldParentType; + + // mark paragraphs tight if needed + if (tight) { + markTightParagraphs(state, listTokIdx); + } + + return true; +}; diff --git a/node_modules/markdown-it/lib/rules_block/paragraph.js b/node_modules/markdown-it/lib/rules_block/paragraph.js new file mode 100644 index 0000000..f0c6872 --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/paragraph.js @@ -0,0 +1,52 @@ +// Paragraph + +'use strict'; + + +module.exports = function paragraph(state, startLine/*, endLine*/) { + var content, terminate, i, l, token, oldParentType, + nextLine = startLine + 1, + terminatorRules = state.md.block.ruler.getRules('paragraph'), + endLine = state.lineMax; + + oldParentType = state.parentType; + state.parentType = 'paragraph'; + + // jump line-by-line until empty one or EOF + for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { + // this would be a code block normally, but after paragraph + // it's considered a lazy continuation regardless of what's there + if (state.sCount[nextLine] - state.blkIndent > 3) { continue; } + + // quirk for blockquotes, this line should already be checked by that rule + if (state.sCount[nextLine] < 0) { continue; } + + // Some tags can terminate paragraph without empty line. + terminate = false; + for (i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + if (terminate) { break; } + } + + content = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); + + state.line = nextLine; + + token = state.push('paragraph_open', 'p', 1); + token.map = [ startLine, state.line ]; + + token = state.push('inline', '', 0); + token.content = content; + token.map = [ startLine, state.line ]; + token.children = []; + + token = state.push('paragraph_close', 'p', -1); + + state.parentType = oldParentType; + + return true; +}; diff --git a/node_modules/markdown-it/lib/rules_block/reference.js b/node_modules/markdown-it/lib/rules_block/reference.js new file mode 100644 index 0000000..78daa26 --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/reference.js @@ -0,0 +1,198 @@ +'use strict'; + + +var normalizeReference = require('../common/utils').normalizeReference; +var isSpace = require('../common/utils').isSpace; + + +module.exports = function reference(state, startLine, _endLine, silent) { + var ch, + destEndPos, + destEndLineNo, + endLine, + href, + i, + l, + label, + labelEnd, + oldParentType, + res, + start, + str, + terminate, + terminatorRules, + title, + lines = 0, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine], + nextLine = startLine + 1; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } + + if (state.src.charCodeAt(pos) !== 0x5B/* [ */) { return false; } + + // Simple check to quickly interrupt scan on [link](url) at the start of line. + // Can be useful on practice: https://github.com/markdown-it/markdown-it/issues/54 + while (++pos < max) { + if (state.src.charCodeAt(pos) === 0x5D /* ] */ && + state.src.charCodeAt(pos - 1) !== 0x5C/* \ */) { + if (pos + 1 === max) { return false; } + if (state.src.charCodeAt(pos + 1) !== 0x3A/* : */) { return false; } + break; + } + } + + endLine = state.lineMax; + + // jump line-by-line until empty one or EOF + terminatorRules = state.md.block.ruler.getRules('reference'); + + oldParentType = state.parentType; + state.parentType = 'reference'; + + for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { + // this would be a code block normally, but after paragraph + // it's considered a lazy continuation regardless of what's there + if (state.sCount[nextLine] - state.blkIndent > 3) { continue; } + + // quirk for blockquotes, this line should already be checked by that rule + if (state.sCount[nextLine] < 0) { continue; } + + // Some tags can terminate paragraph without empty line. + terminate = false; + for (i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + if (terminate) { break; } + } + + str = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); + max = str.length; + + for (pos = 1; pos < max; pos++) { + ch = str.charCodeAt(pos); + if (ch === 0x5B /* [ */) { + return false; + } else if (ch === 0x5D /* ] */) { + labelEnd = pos; + break; + } else if (ch === 0x0A /* \n */) { + lines++; + } else if (ch === 0x5C /* \ */) { + pos++; + if (pos < max && str.charCodeAt(pos) === 0x0A) { + lines++; + } + } + } + + if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A/* : */) { return false; } + + // [label]: destination 'title' + // ^^^ skip optional whitespace here + for (pos = labelEnd + 2; pos < max; pos++) { + ch = str.charCodeAt(pos); + if (ch === 0x0A) { + lines++; + } else if (isSpace(ch)) { + /*eslint no-empty:0*/ + } else { + break; + } + } + + // [label]: destination 'title' + // ^^^^^^^^^^^ parse this + res = state.md.helpers.parseLinkDestination(str, pos, max); + if (!res.ok) { return false; } + + href = state.md.normalizeLink(res.str); + if (!state.md.validateLink(href)) { return false; } + + pos = res.pos; + lines += res.lines; + + // save cursor state, we could require to rollback later + destEndPos = pos; + destEndLineNo = lines; + + // [label]: destination 'title' + // ^^^ skipping those spaces + start = pos; + for (; pos < max; pos++) { + ch = str.charCodeAt(pos); + if (ch === 0x0A) { + lines++; + } else if (isSpace(ch)) { + /*eslint no-empty:0*/ + } else { + break; + } + } + + // [label]: destination 'title' + // ^^^^^^^ parse this + res = state.md.helpers.parseLinkTitle(str, pos, max); + if (pos < max && start !== pos && res.ok) { + title = res.str; + pos = res.pos; + lines += res.lines; + } else { + title = ''; + pos = destEndPos; + lines = destEndLineNo; + } + + // skip trailing spaces until the rest of the line + while (pos < max) { + ch = str.charCodeAt(pos); + if (!isSpace(ch)) { break; } + pos++; + } + + if (pos < max && str.charCodeAt(pos) !== 0x0A) { + if (title) { + // garbage at the end of the line after title, + // but it could still be a valid reference if we roll back + title = ''; + pos = destEndPos; + lines = destEndLineNo; + while (pos < max) { + ch = str.charCodeAt(pos); + if (!isSpace(ch)) { break; } + pos++; + } + } + } + + if (pos < max && str.charCodeAt(pos) !== 0x0A) { + // garbage at the end of the line + return false; + } + + label = normalizeReference(str.slice(1, labelEnd)); + if (!label) { + // CommonMark 0.20 disallows empty labels + return false; + } + + // Reference can not terminate anything. This check is for safety only. + /*istanbul ignore if*/ + if (silent) { return true; } + + if (typeof state.env.references === 'undefined') { + state.env.references = {}; + } + if (typeof state.env.references[label] === 'undefined') { + state.env.references[label] = { title: title, href: href }; + } + + state.parentType = oldParentType; + + state.line = startLine + lines + 1; + return true; +}; diff --git a/node_modules/markdown-it/lib/rules_block/state_block.js b/node_modules/markdown-it/lib/rules_block/state_block.js new file mode 100644 index 0000000..e42cb4b --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/state_block.js @@ -0,0 +1,231 @@ +// Parser state class + +'use strict'; + +var Token = require('../token'); +var isSpace = require('../common/utils').isSpace; + + +function StateBlock(src, md, env, tokens) { + var ch, s, start, pos, len, indent, offset, indent_found; + + this.src = src; + + // link to parser instance + this.md = md; + + this.env = env; + + // + // Internal state vartiables + // + + this.tokens = tokens; + + this.bMarks = []; // line begin offsets for fast jumps + this.eMarks = []; // line end offsets for fast jumps + this.tShift = []; // offsets of the first non-space characters (tabs not expanded) + this.sCount = []; // indents for each line (tabs expanded) + + // An amount of virtual spaces (tabs expanded) between beginning + // of each line (bMarks) and real beginning of that line. + // + // It exists only as a hack because blockquotes override bMarks + // losing information in the process. + // + // It's used only when expanding tabs, you can think about it as + // an initial tab length, e.g. bsCount=21 applied to string `\t123` + // means first tab should be expanded to 4-21%4 === 3 spaces. + // + this.bsCount = []; + + // block parser variables + this.blkIndent = 0; // required block content indent (for example, if we are + // inside a list, it would be positioned after list marker) + this.line = 0; // line index in src + this.lineMax = 0; // lines count + this.tight = false; // loose/tight mode for lists + this.ddIndent = -1; // indent of the current dd block (-1 if there isn't any) + this.listIndent = -1; // indent of the current list block (-1 if there isn't any) + + // can be 'blockquote', 'list', 'root', 'paragraph' or 'reference' + // used in lists to determine if they interrupt a paragraph + this.parentType = 'root'; + + this.level = 0; + + // renderer + this.result = ''; + + // Create caches + // Generate markers. + s = this.src; + indent_found = false; + + for (start = pos = indent = offset = 0, len = s.length; pos < len; pos++) { + ch = s.charCodeAt(pos); + + if (!indent_found) { + if (isSpace(ch)) { + indent++; + + if (ch === 0x09) { + offset += 4 - offset % 4; + } else { + offset++; + } + continue; + } else { + indent_found = true; + } + } + + if (ch === 0x0A || pos === len - 1) { + if (ch !== 0x0A) { pos++; } + this.bMarks.push(start); + this.eMarks.push(pos); + this.tShift.push(indent); + this.sCount.push(offset); + this.bsCount.push(0); + + indent_found = false; + indent = 0; + offset = 0; + start = pos + 1; + } + } + + // Push fake entry to simplify cache bounds checks + this.bMarks.push(s.length); + this.eMarks.push(s.length); + this.tShift.push(0); + this.sCount.push(0); + this.bsCount.push(0); + + this.lineMax = this.bMarks.length - 1; // don't count last fake line +} + +// Push new token to "stream". +// +StateBlock.prototype.push = function (type, tag, nesting) { + var token = new Token(type, tag, nesting); + token.block = true; + + if (nesting < 0) this.level--; // closing tag + token.level = this.level; + if (nesting > 0) this.level++; // opening tag + + this.tokens.push(token); + return token; +}; + +StateBlock.prototype.isEmpty = function isEmpty(line) { + return this.bMarks[line] + this.tShift[line] >= this.eMarks[line]; +}; + +StateBlock.prototype.skipEmptyLines = function skipEmptyLines(from) { + for (var max = this.lineMax; from < max; from++) { + if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) { + break; + } + } + return from; +}; + +// Skip spaces from given position. +StateBlock.prototype.skipSpaces = function skipSpaces(pos) { + var ch; + + for (var max = this.src.length; pos < max; pos++) { + ch = this.src.charCodeAt(pos); + if (!isSpace(ch)) { break; } + } + return pos; +}; + +// Skip spaces from given position in reverse. +StateBlock.prototype.skipSpacesBack = function skipSpacesBack(pos, min) { + if (pos <= min) { return pos; } + + while (pos > min) { + if (!isSpace(this.src.charCodeAt(--pos))) { return pos + 1; } + } + return pos; +}; + +// Skip char codes from given position +StateBlock.prototype.skipChars = function skipChars(pos, code) { + for (var max = this.src.length; pos < max; pos++) { + if (this.src.charCodeAt(pos) !== code) { break; } + } + return pos; +}; + +// Skip char codes reverse from given position - 1 +StateBlock.prototype.skipCharsBack = function skipCharsBack(pos, code, min) { + if (pos <= min) { return pos; } + + while (pos > min) { + if (code !== this.src.charCodeAt(--pos)) { return pos + 1; } + } + return pos; +}; + +// cut lines range from source. +StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF) { + var i, lineIndent, ch, first, last, queue, lineStart, + line = begin; + + if (begin >= end) { + return ''; + } + + queue = new Array(end - begin); + + for (i = 0; line < end; line++, i++) { + lineIndent = 0; + lineStart = first = this.bMarks[line]; + + if (line + 1 < end || keepLastLF) { + // No need for bounds check because we have fake entry on tail. + last = this.eMarks[line] + 1; + } else { + last = this.eMarks[line]; + } + + while (first < last && lineIndent < indent) { + ch = this.src.charCodeAt(first); + + if (isSpace(ch)) { + if (ch === 0x09) { + lineIndent += 4 - (lineIndent + this.bsCount[line]) % 4; + } else { + lineIndent++; + } + } else if (first - lineStart < this.tShift[line]) { + // patched tShift masked characters to look like spaces (blockquotes, list markers) + lineIndent++; + } else { + break; + } + + first++; + } + + if (lineIndent > indent) { + // partially expanding tabs in code blocks, e.g '\t\tfoobar' + // with indent=2 becomes ' \tfoobar' + queue[i] = new Array(lineIndent - indent + 1).join(' ') + this.src.slice(first, last); + } else { + queue[i] = this.src.slice(first, last); + } + } + + return queue.join(''); +}; + +// re-export Token class to use in block rules +StateBlock.prototype.Token = Token; + + +module.exports = StateBlock; diff --git a/node_modules/markdown-it/lib/rules_block/table.js b/node_modules/markdown-it/lib/rules_block/table.js new file mode 100644 index 0000000..7d9208f --- /dev/null +++ b/node_modules/markdown-it/lib/rules_block/table.js @@ -0,0 +1,221 @@ +// GFM table, https://github.github.com/gfm/#tables-extension- + +'use strict'; + +var isSpace = require('../common/utils').isSpace; + + +function getLine(state, line) { + var pos = state.bMarks[line] + state.tShift[line], + max = state.eMarks[line]; + + return state.src.substr(pos, max - pos); +} + +function escapedSplit(str) { + var result = [], + pos = 0, + max = str.length, + ch, + isEscaped = false, + lastPos = 0, + current = ''; + + ch = str.charCodeAt(pos); + + while (pos < max) { + if (ch === 0x7c/* | */) { + if (!isEscaped) { + // pipe separating cells, '|' + result.push(current + str.substring(lastPos, pos)); + current = ''; + lastPos = pos + 1; + } else { + // escaped pipe, '\|' + current += str.substring(lastPos, pos - 1); + lastPos = pos; + } + } + + isEscaped = (ch === 0x5c/* \ */); + pos++; + + ch = str.charCodeAt(pos); + } + + result.push(current + str.substring(lastPos)); + + return result; +} + + +module.exports = function table(state, startLine, endLine, silent) { + var ch, lineText, pos, i, l, nextLine, columns, columnCount, token, + aligns, t, tableLines, tbodyLines, oldParentType, terminate, + terminatorRules, firstCh, secondCh; + + // should have at least two lines + if (startLine + 2 > endLine) { return false; } + + nextLine = startLine + 1; + + if (state.sCount[nextLine] < state.blkIndent) { return false; } + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[nextLine] - state.blkIndent >= 4) { return false; } + + // first character of the second line should be '|', '-', ':', + // and no other characters are allowed but spaces; + // basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp + + pos = state.bMarks[nextLine] + state.tShift[nextLine]; + if (pos >= state.eMarks[nextLine]) { return false; } + + firstCh = state.src.charCodeAt(pos++); + if (firstCh !== 0x7C/* | */ && firstCh !== 0x2D/* - */ && firstCh !== 0x3A/* : */) { return false; } + + if (pos >= state.eMarks[nextLine]) { return false; } + + secondCh = state.src.charCodeAt(pos++); + if (secondCh !== 0x7C/* | */ && secondCh !== 0x2D/* - */ && secondCh !== 0x3A/* : */ && !isSpace(secondCh)) { + return false; + } + + // if first character is '-', then second character must not be a space + // (due to parsing ambiguity with list) + if (firstCh === 0x2D/* - */ && isSpace(secondCh)) { return false; } + + while (pos < state.eMarks[nextLine]) { + ch = state.src.charCodeAt(pos); + + if (ch !== 0x7C/* | */ && ch !== 0x2D/* - */ && ch !== 0x3A/* : */ && !isSpace(ch)) { return false; } + + pos++; + } + + lineText = getLine(state, startLine + 1); + + columns = lineText.split('|'); + aligns = []; + for (i = 0; i < columns.length; i++) { + t = columns[i].trim(); + if (!t) { + // allow empty columns before and after table, but not in between columns; + // e.g. allow ` |---| `, disallow ` ---||--- ` + if (i === 0 || i === columns.length - 1) { + continue; + } else { + return false; + } + } + + if (!/^:?-+:?$/.test(t)) { return false; } + if (t.charCodeAt(t.length - 1) === 0x3A/* : */) { + aligns.push(t.charCodeAt(0) === 0x3A/* : */ ? 'center' : 'right'); + } else if (t.charCodeAt(0) === 0x3A/* : */) { + aligns.push('left'); + } else { + aligns.push(''); + } + } + + lineText = getLine(state, startLine).trim(); + if (lineText.indexOf('|') === -1) { return false; } + if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } + columns = escapedSplit(lineText); + if (columns.length && columns[0] === '') columns.shift(); + if (columns.length && columns[columns.length - 1] === '') columns.pop(); + + // header row will define an amount of columns in the entire table, + // and align row should be exactly the same (the rest of the rows can differ) + columnCount = columns.length; + if (columnCount === 0 || columnCount !== aligns.length) { return false; } + + if (silent) { return true; } + + oldParentType = state.parentType; + state.parentType = 'table'; + + // use 'blockquote' lists for termination because it's + // the most similar to tables + terminatorRules = state.md.block.ruler.getRules('blockquote'); + + token = state.push('table_open', 'table', 1); + token.map = tableLines = [ startLine, 0 ]; + + token = state.push('thead_open', 'thead', 1); + token.map = [ startLine, startLine + 1 ]; + + token = state.push('tr_open', 'tr', 1); + token.map = [ startLine, startLine + 1 ]; + + for (i = 0; i < columns.length; i++) { + token = state.push('th_open', 'th', 1); + if (aligns[i]) { + token.attrs = [ [ 'style', 'text-align:' + aligns[i] ] ]; + } + + token = state.push('inline', '', 0); + token.content = columns[i].trim(); + token.children = []; + + token = state.push('th_close', 'th', -1); + } + + token = state.push('tr_close', 'tr', -1); + token = state.push('thead_close', 'thead', -1); + + for (nextLine = startLine + 2; nextLine < endLine; nextLine++) { + if (state.sCount[nextLine] < state.blkIndent) { break; } + + terminate = false; + for (i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + + if (terminate) { break; } + lineText = getLine(state, nextLine).trim(); + if (!lineText) { break; } + if (state.sCount[nextLine] - state.blkIndent >= 4) { break; } + columns = escapedSplit(lineText); + if (columns.length && columns[0] === '') columns.shift(); + if (columns.length && columns[columns.length - 1] === '') columns.pop(); + + if (nextLine === startLine + 2) { + token = state.push('tbody_open', 'tbody', 1); + token.map = tbodyLines = [ startLine + 2, 0 ]; + } + + token = state.push('tr_open', 'tr', 1); + token.map = [ nextLine, nextLine + 1 ]; + + for (i = 0; i < columnCount; i++) { + token = state.push('td_open', 'td', 1); + if (aligns[i]) { + token.attrs = [ [ 'style', 'text-align:' + aligns[i] ] ]; + } + + token = state.push('inline', '', 0); + token.content = columns[i] ? columns[i].trim() : ''; + token.children = []; + + token = state.push('td_close', 'td', -1); + } + token = state.push('tr_close', 'tr', -1); + } + + if (tbodyLines) { + token = state.push('tbody_close', 'tbody', -1); + tbodyLines[1] = nextLine; + } + + token = state.push('table_close', 'table', -1); + tableLines[1] = nextLine; + + state.parentType = oldParentType; + state.line = nextLine; + return true; +}; |