/** * marked v14.1.2 - a markdown parser * Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed) * https://github.com/markedjs/marked */ /** * DO NOT EDIT THIS FILE * The code in this file is generated from files in ./src/ */ 'use strict'; /** * Gets the original marked default options. */function _getDefaults() { return { async: false, breaks: false, extensions: null, gfm: true, hooks: null, pedantic: false, renderer: null, silent: false, tokenizer: null, walkTokens: null, };}exports.defaults = _getDefaults();function changeDefaults(newDefaults) { exports.defaults = newDefaults;} /** * Helpers */const escapeTest = /[&<>"']/;const escapeReplace = new RegExp(escapeTest.source, 'g');const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, 'g');const escapeReplacements = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''',};const getEscapeReplacement = (ch) => escapeReplacements[ch];function escape$1(html, encode) { if (encode) { if (escapeTest.test(html)) { return html.replace(escapeReplace, getEscapeReplacement); } } else { if (escapeTestNoEncode.test(html)) { return html.replace(escapeReplaceNoEncode, getEscapeReplacement); } } return html;}const caret = /(^|[^\[])\^/g;function edit(regex, opt) { let source = typeof regex === 'string' ? regex : regex.source; opt = opt || ''; const obj = { replace: (name, val) => { let valSource = typeof val === 'string' ? val : val.source; valSource = valSource.replace(caret, '$1'); source = source.replace(name, valSource); return obj; }, getRegex: () => { return new RegExp(source, opt); }, }; return obj;}function cleanUrl(href) { try { href = encodeURI(href).replace(/%25/g, '%'); } catch { return null; } return href;}const noopTest = { exec: () => null };function splitCells(tableRow, count) { // ensure that every cell-delimiting pipe has a space // before it to distinguish it from an escaped pipe const row = tableRow.replace(/\|/g, (match, offset, str) => { let escaped = false; let curr = offset; while (--curr >= 0 && str[curr] === '\\') escaped = !escaped; if (escaped) { // odd number of slashes means | is escaped // so we leave it alone return '|'; } else { // add space before unescaped | return ' |'; } }), cells = row.split(/ \|/); let i = 0; // First/last cell in a row cannot be empty if it has no leading/trailing pipe if (!cells[0].trim()) { cells.shift(); } if (cells.length > 0 && !cells[cells.length - 1].trim()) { cells.pop(); } if (count) { if (cells.length > count) { cells.splice(count); } else { while (cells.length < count) cells.push(''); } } for (; i < cells.length; i++) { // leading or trailing whitespace is ignored per the gfm spec cells[i] = cells[i].trim().replace(/\\\|/g, '|'); } return cells;}/** * Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). * /c*$/ is vulnerable to REDOS. * * @param str * @param c * @param invert Remove suffix of non-c chars instead. Default falsey. */function rtrim(str, c, invert) { const l = str.length; if (l === 0) { return ''; } // Length of suffix matching the invert condition. let suffLen = 0; // Step left until we fail to match the invert condition. while (suffLen < l) { const currChar = str.charAt(l - suffLen - 1); if (currChar === c && !invert) { suffLen++; } else if (currChar !== c && invert) { suffLen++; } else { break; } } return str.slice(0, l - suffLen);}function findClosingBracket(str, b) { if (str.indexOf(b[1]) === -1) { return -1; } let level = 0; for (let i = 0; i < str.length; i++) { if (str[i] === '\\') { i++; } else if (str[i] === b[0]) { level++; } else if (str[i] === b[1]) { level--; if (level < 0) { return i; } } } return -1;} function outputLink(cap, link, raw, lexer) { const href = link.href; const title = link.title ? escape$1(link.title) : null; const text = cap[1].replace(/\\([\[\]])/g, '$1'); if (cap[0].charAt(0) !== '!') { lexer.state.inLink = true; const token = { type: 'link', raw, href, title, text, tokens: lexer.inlineTokens(text), }; lexer.state.inLink = false; return token; } return { type: 'image', raw, href, title, text: escape$1(text), };}function indentCodeCompensation(raw, text) { const matchIndentToCode = raw.match(/^(\s+)(?:```)/); if (matchIndentToCode === null) { return text; } const indentToCode = matchIndentToCode[1]; return text .split('\n') .map(node => { const matchIndentInNode = node.match(/^\s+/); if (matchIndentInNode === null) { return node; } const [indentInNode] = matchIndentInNode; if (indentInNode.length >= indentToCode.length) { return node.slice(indentToCode.length); } return node; }) .join('\n');}/** * Tokenizer */class _Tokenizer { options; rules; // set by the lexer lexer; // set by the lexer constructor(options) { this.options = options || exports.defaults; } space(src) { const cap = this.rules.block.newline.exec(src); if (cap && cap[0].length > 0) { return { type: 'space', raw: cap[0], }; } } code(src) { const cap = this.rules.block.code.exec(src); if (cap) { const text = cap[0].replace(/^(?: {1,4}| {0,3}\t)/gm, ''); return { type: 'code', raw: cap[0], codeBlockStyle: 'indented', text: !this.options.pedantic ? rtrim(text, '\n') : text, }; } } fences(src) { const cap = this.rules.block.fences.exec(src); if (cap) { const raw = cap[0]; const text = indentCodeCompensation(raw, cap[3] || ''); return { type: 'code', raw, lang: cap[2] ? cap[2].trim().replace(this.rules.inline.anyPunctuation, '$1') : cap[2], text, }; } } heading(src) { const cap = this.rules.block.heading.exec(src); if (cap) { let text = cap[2].trim(); // remove trailing #s if (/#$/.test(text)) { const trimmed = rtrim(text, '#'); if (this.options.pedantic) { text = trimmed.trim(); } else if (!trimmed || / $/.test(trimmed)) { // CommonMark requires space before trailing #s text = trimmed.trim(); } } return { type: 'heading', raw: cap[0], depth: cap[1].length, text, tokens: this.lexer.inline(text), }; } } hr(src) { const cap = this.rules.block.hr.exec(src); if (cap) { return { type: 'hr', raw: rtrim(cap[0], '\n'), }; } } blockquote(src) { const cap = this.rules.block.blockquote.exec(src); if (cap) { let lines = rtrim(cap[0], '\n').split('\n'); let raw = ''; let text = ''; const tokens = []; while (lines.length > 0) { let inBlockquote = false; const currentLines = []; let i; for (i = 0; i < lines.length; i++) { // get lines up to a continuation if (/^ {0,3}>/.test(lines[i])) { currentLines.push(lines[i]); inBlockquote = true; } else if (!inBlockquote) { currentLines.push(lines[i]); } else { break; } } lines = lines.slice(i); const currentRaw = currentLines.join('\n'); const currentText = currentRaw // precede setext continuation with 4 spaces so it isn't a setext .replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g, '\n $1') .replace(/^ {0,3}>[ \t]?/gm, ''); raw = raw ? `${raw}\n${currentRaw}` : currentRaw; text = text ? `${text}\n${currentText}` : currentText; // parse blockquote lines as top level tokens // merge paragraphs if this is a continuation const top = this.lexer.state.top; this.lexer.state.top = true; this.lexer.blockTokens(currentText, tokens, true); this.lexer.state.top = top; // if there is no continuation then we are done if (lines.length === 0) { break; } const lastToken = tokens[tokens.length - 1]; if (lastToken?.type === 'code') { // blockquote continuation cannot be preceded by a code block break; } else if (lastToken?.type === 'blockquote') { // include continuation in nested blockquote const oldToken = lastToken; const newText = oldToken.raw + '\n' + lines.join('\n'); const newToken = this.blockquote(newText); tokens[tokens.length - 1] = newToken; raw = raw.substring(0, raw.length - oldToken.raw.length) + newToken.raw; text = text.substring(0, text.length - oldToken.text.length) + newToken.text; break; } else if (lastToken?.type === 'list') { // include continuation in nested list const oldToken = lastToken; const newText = oldToken.raw + '\n' + lines.join('\n'); const newToken = this.list(newText); tokens[tokens.length - 1] = newToken; raw = raw.substring(0, raw.length - lastToken.raw.length) + newToken.raw; text = text.substring(0, text.length - oldToken.raw.length) + newToken.raw; lines = newText.substring(tokens[tokens.length - 1].raw.length).split('\n'); continue; } } return { type: 'blockquote', raw, tokens, text, }; } } list(src) { let cap = this.rules.block.list.exec(src); if (cap) { let bull = cap[1].trim(); const isordered = bull.length > 1; const list = { type: 'list', raw: '', ordered: isordered, start: isordered ? +bull.slice(0, -1) : '', loose: false, items: [], }; bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`; if (this.options.pedantic) { bull = isordered ? bull : '[*+-]'; } // Get next list item const itemRegex = new RegExp(`^( {0,3}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`); let endsWithBlankLine = false; // Check if current bullet point can start a new List Item while (src) { let endEarly = false; let raw = ''; let itemContents = ''; if (!(cap = itemRegex.exec(src))) { break; } if (this.rules.block.hr.test(src)) { // End list if bullet was actually HR (possibly move into itemRegex?) break; } raw = cap[0]; src = src.substring(raw.length); let line = cap[2].split('\n', 1)[0].replace(/^\t+/, (t) => ' '.repeat(3 * t.length)); let nextLine = src.split('\n', 1)[0]; let blankLine = !line.trim(); let indent = 0; if (this.options.pedantic) { indent = 2; itemContents = line.trimStart(); } else if (blankLine) { indent = cap[1].length + 1; } else { indent = cap[2].search(/[^ ]/); // Find first non-space char indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent itemContents = line.slice(indent); indent += cap[1].length; } if (blankLine && /^[ \t]*$/.test(nextLine)) { // Items begin with at most one blank line raw += nextLine + '\n'; src = src.substring(nextLine.length + 1); endEarly = true; } if (!endEarly) { const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`); const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`); const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`); const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`); const htmlBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}<[a-z].*>`, 'i'); // Check if following lines should be included in List Item while (src) { const rawLine = src.split('\n', 1)[0]; let nextLineWithoutTabs; nextLine = rawLine; // Re-align to follow commonmark nesting rules if (this.options.pedantic) { nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); nextLineWithoutTabs = nextLine; } else { nextLineWithoutTabs = nextLine.replace(/\t/g, ' '); } // End list item if found code fences if (fencesBeginRegex.test(nextLine)) { break; } // End list item if found start of new heading if (headingBeginRegex.test(nextLine)) { break; } // End list item if found start of html block if (htmlBeginRegex.test(nextLine)) { break; } // End list item if found start of new bullet if (nextBulletRegex.test(nextLine)) { break; } // Horizontal rule found if (hrRegex.test(nextLine)) { break; } if (nextLineWithoutTabs.search(/[^ ]/) >= indent || !nextLine.trim()) { // Dedent if possible itemContents += '\n' + nextLineWithoutTabs.slice(indent); } else { // not enough indentation if (blankLine) { break; } // paragraph continuation unless last line was a different block level element if (line.replace(/\t/g, ' ').search(/[^ ]/) >= 4) { // indented code block break; } if (fencesBeginRegex.test(line)) { break; } if (headingBeginRegex.test(line)) { break; } if (hrRegex.test(line)) { break; }Lines 1–500 of 2521. Load more
/** * marked v14.1.2 - a markdown parser * Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed) * https://github.com/markedjs/marked */ /** * DO NOT EDIT THIS FILE * The code in this file is generated from files in ./src/ */ 'use strict'; /** * Gets the original marked default options. */ function _getDefaults() { return { async: false, breaks: false, extensions: null, gfm: true, hooks: null, pedantic: false, renderer: null, silent: false, tokenizer: null, walkTokens: null, }; } exports.defaults = _getDefaults(); function changeDefaults(newDefaults) { exports.defaults = newDefaults; } /** * Helpers */ const escapeTest = /[&<>"']/; const escapeReplace = new RegExp(escapeTest.source, 'g'); const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/; const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, 'g'); const escapeReplacements = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', }; const getEscapeReplacement = (ch) => escapeReplacements[ch]; function escape$1(html, encode) { if (encode) { if (escapeTest.test(html)) { return html.replace(escapeReplace, getEscapeReplacement); } } else { if (escapeTestNoEncode.test(html)) { return html.replace(escapeReplaceNoEncode, getEscapeReplacement); } } return html; } const caret = /(^|[^\[])\^/g; function edit(regex, opt) { let source = typeof regex === 'string' ? regex : regex.source; opt = opt || ''; const obj = { replace: (name, val) => { let valSource = typeof val === 'string' ? val : val.source; valSource = valSource.replace(caret, '$1'); source = source.replace(name, valSource); return obj; }, getRegex: () => { return new RegExp(source, opt); }, }; return obj; } function cleanUrl(href) { try { href = encodeURI(href).replace(/%25/g, '%'); } catch { return null; } return href; } const noopTest = { exec: () => null }; function splitCells(tableRow, count) { // ensure that every cell-delimiting pipe has a space // before it to distinguish it from an escaped pipe const row = tableRow.replace(/\|/g, (match, offset, str) => { let escaped = false; let curr = offset; while (--curr >= 0 && str[curr] === '\\') escaped = !escaped; if (escaped) { // odd number of slashes means | is escaped // so we leave it alone return '|'; } else { // add space before unescaped | return ' |'; } }), cells = row.split(/ \|/); let i = 0; // First/last cell in a row cannot be empty if it has no leading/trailing pipe if (!cells[0].trim()) { cells.shift(); } if (cells.length > 0 && !cells[cells.length - 1].trim()) { cells.pop(); } if (count) { if (cells.length > count) { cells.splice(count); } else { while (cells.length < count) cells.push(''); } } for (; i < cells.length; i++) { // leading or trailing whitespace is ignored per the gfm spec cells[i] = cells[i].trim().replace(/\\\|/g, '|'); } return cells; } /** * Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). * /c*$/ is vulnerable to REDOS. * * @param str * @param c * @param invert Remove suffix of non-c chars instead. Default falsey. */ function rtrim(str, c, invert) { const l = str.length; if (l === 0) { return ''; } // Length of suffix matching the invert condition. let suffLen = 0; // Step left until we fail to match the invert condition. while (suffLen < l) { const currChar = str.charAt(l - suffLen - 1); if (currChar === c && !invert) { suffLen++; } else if (currChar !== c && invert) { suffLen++; } else { break; } } return str.slice(0, l - suffLen); } function findClosingBracket(str, b) { if (str.indexOf(b[1]) === -1) { return -1; } let level = 0; for (let i = 0; i < str.length; i++) { if (str[i] === '\\') { i++; } else if (str[i] === b[0]) { level++; } else if (str[i] === b[1]) { level--; if (level < 0) { return i; } } } return -1; } function outputLink(cap, link, raw, lexer) { const href = link.href; const title = link.title ? escape$1(link.title) : null; const text = cap[1].replace(/\\([\[\]])/g, '$1'); if (cap[0].charAt(0) !== '!') { lexer.state.inLink = true; const token = { type: 'link', raw, href, title, text, tokens: lexer.inlineTokens(text), }; lexer.state.inLink = false; return token; } return { type: 'image', raw, href, title, text: escape$1(text), }; } function indentCodeCompensation(raw, text) { const matchIndentToCode = raw.match(/^(\s+)(?:```)/); if (matchIndentToCode === null) { return text; } const indentToCode = matchIndentToCode[1]; return text .split('\n') .map(node => { const matchIndentInNode = node.match(/^\s+/); if (matchIndentInNode === null) { return node; } const [indentInNode] = matchIndentInNode; if (indentInNode.length >= indentToCode.length) { return node.slice(indentToCode.length); } return node; }) .join('\n'); } /** * Tokenizer */ class _Tokenizer { options; rules; // set by the lexer lexer; // set by the lexer constructor(options) { this.options = options || exports.defaults; } space(src) { const cap = this.rules.block.newline.exec(src); if (cap && cap[0].length > 0) { return { type: 'space', raw: cap[0], }; } } code(src) { const cap = this.rules.block.code.exec(src); if (cap) { const text = cap[0].replace(/^(?: {1,4}| {0,3}\t)/gm, ''); return { type: 'code', raw: cap[0], codeBlockStyle: 'indented', text: !this.options.pedantic ? rtrim(text, '\n') : text, }; } } fences(src) { const cap = this.rules.block.fences.exec(src); if (cap) { const raw = cap[0]; const text = indentCodeCompensation(raw, cap[3] || ''); return { type: 'code', raw, lang: cap[2] ? cap[2].trim().replace(this.rules.inline.anyPunctuation, '$1') : cap[2], text, }; } } heading(src) { const cap = this.rules.block.heading.exec(src); if (cap) { let text = cap[2].trim(); // remove trailing #s if (/#$/.test(text)) { const trimmed = rtrim(text, '#'); if (this.options.pedantic) { text = trimmed.trim(); } else if (!trimmed || / $/.test(trimmed)) { // CommonMark requires space before trailing #s text = trimmed.trim(); } } return { type: 'heading', raw: cap[0], depth: cap[1].length, text, tokens: this.lexer.inline(text), }; } } hr(src) { const cap = this.rules.block.hr.exec(src); if (cap) { return { type: 'hr', raw: rtrim(cap[0], '\n'), }; } } blockquote(src) { const cap = this.rules.block.blockquote.exec(src); if (cap) { let lines = rtrim(cap[0], '\n').split('\n'); let raw = ''; let text = ''; const tokens = []; while (lines.length > 0) { let inBlockquote = false; const currentLines = []; let i; for (i = 0; i < lines.length; i++) { // get lines up to a continuation if (/^ {0,3}>/.test(lines[i])) { currentLines.push(lines[i]); inBlockquote = true; } else if (!inBlockquote) { currentLines.push(lines[i]); } else { break; } } lines = lines.slice(i); const currentRaw = currentLines.join('\n'); const currentText = currentRaw // precede setext continuation with 4 spaces so it isn't a setext .replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g, '\n $1') .replace(/^ {0,3}>[ \t]?/gm, ''); raw = raw ? `${raw}\n${currentRaw}` : currentRaw; text = text ? `${text}\n${currentText}` : currentText; // parse blockquote lines as top level tokens // merge paragraphs if this is a continuation const top = this.lexer.state.top; this.lexer.state.top = true; this.lexer.blockTokens(currentText, tokens, true); this.lexer.state.top = top; // if there is no continuation then we are done if (lines.length === 0) { break; } const lastToken = tokens[tokens.length - 1]; if (lastToken?.type === 'code') { // blockquote continuation cannot be preceded by a code block break; } else if (lastToken?.type === 'blockquote') { // include continuation in nested blockquote const oldToken = lastToken; const newText = oldToken.raw + '\n' + lines.join('\n'); const newToken = this.blockquote(newText); tokens[tokens.length - 1] = newToken; raw = raw.substring(0, raw.length - oldToken.raw.length) + newToken.raw; text = text.substring(0, text.length - oldToken.text.length) + newToken.text; break; } else if (lastToken?.type === 'list') { // include continuation in nested list const oldToken = lastToken; const newText = oldToken.raw + '\n' + lines.join('\n'); const newToken = this.list(newText); tokens[tokens.length - 1] = newToken; raw = raw.substring(0, raw.length - lastToken.raw.length) + newToken.raw; text = text.substring(0, text.length - oldToken.raw.length) + newToken.raw; lines = newText.substring(tokens[tokens.length - 1].raw.length).split('\n'); continue; } } return { type: 'blockquote', raw, tokens, text, }; } } list(src) { let cap = this.rules.block.list.exec(src); if (cap) { let bull = cap[1].trim(); const isordered = bull.length > 1; const list = { type: 'list', raw: '', ordered: isordered, start: isordered ? +bull.slice(0, -1) : '', loose: false, items: [], }; bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`; if (this.options.pedantic) { bull = isordered ? bull : '[*+-]'; } // Get next list item const itemRegex = new RegExp(`^( {0,3}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`); let endsWithBlankLine = false; // Check if current bullet point can start a new List Item while (src) { let endEarly = false; let raw = ''; let itemContents = ''; if (!(cap = itemRegex.exec(src))) { break; } if (this.rules.block.hr.test(src)) { // End list if bullet was actually HR (possibly move into itemRegex?) break; } raw = cap[0]; src = src.substring(raw.length); let line = cap[2].split('\n', 1)[0].replace(/^\t+/, (t) => ' '.repeat(3 * t.length)); let nextLine = src.split('\n', 1)[0]; let blankLine = !line.trim(); let indent = 0; if (this.options.pedantic) { indent = 2; itemContents = line.trimStart(); } else if (blankLine) { indent = cap[1].length + 1; } else { indent = cap[2].search(/[^ ]/); // Find first non-space char indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent itemContents = line.slice(indent); indent += cap[1].length; } if (blankLine && /^[ \t]*$/.test(nextLine)) { // Items begin with at most one blank line raw += nextLine + '\n'; src = src.substring(nextLine.length + 1); endEarly = true; } if (!endEarly) { const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`); const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`); const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`); const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`); const htmlBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}<[a-z].*>`, 'i'); // Check if following lines should be included in List Item while (src) { const rawLine = src.split('\n', 1)[0]; let nextLineWithoutTabs; nextLine = rawLine; // Re-align to follow commonmark nesting rules if (this.options.pedantic) { nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); nextLineWithoutTabs = nextLine; } else { nextLineWithoutTabs = nextLine.replace(/\t/g, ' '); } // End list item if found code fences if (fencesBeginRegex.test(nextLine)) { break; } // End list item if found start of new heading if (headingBeginRegex.test(nextLine)) { break; } // End list item if found start of html block if (htmlBeginRegex.test(nextLine)) { break; } // End list item if found start of new bullet if (nextBulletRegex.test(nextLine)) { break; } // Horizontal rule found if (hrRegex.test(nextLine)) { break; } if (nextLineWithoutTabs.search(/[^ ]/) >= indent || !nextLine.trim()) { // Dedent if possible itemContents += '\n' + nextLineWithoutTabs.slice(indent); } else { // not enough indentation if (blankLine) { break; } // paragraph continuation unless last line was a different block level element if (line.replace(/\t/g, ' ').search(/[^ ]/) >= 4) { // indented code block break; } if (fencesBeginRegex.test(line)) { break; } if (headingBeginRegex.test(line)) { break; } if (hrRegex.test(line)) { break; }