diff --git a/lib/common/html_blocks.js b/lib/common/html_blocks.js index 96848e0..d13c17d 100644 --- a/lib/common/html_blocks.js +++ b/lib/common/html_blocks.js @@ -3,60 +3,65 @@ 'use strict'; -var html_blocks = {}; -[ +module.exports = [ + 'address', 'article', 'aside', - 'button', + 'base', + 'basefont', 'blockquote', 'body', - 'canvas', 'caption', + 'center', 'col', 'colgroup', 'dd', + 'details', + 'dialog', + 'dir', 'div', 'dl', 'dt', - 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', + 'frame', + 'frameset', 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', + 'head', 'header', - 'hgroup', 'hr', - 'iframe', + 'html', + 'legend', 'li', - 'map', - 'object', + 'link', + 'main', + 'menu', + 'menuitem', + 'meta', + 'nav', + 'noframes', 'ol', - 'output', + 'optgroup', + 'option', 'p', + 'param', 'pre', - 'progress', - 'script', 'section', - 'style', + 'source', + 'title', + 'summary', 'table', 'tbody', 'td', - 'textarea', 'tfoot', 'th', - 'tr', 'thead', - 'ul', - 'video' -].forEach(function (name) { html_blocks[name] = true; }); - - -module.exports = html_blocks; + 'title', + 'tr', + 'track', + 'ul' +]; diff --git a/lib/common/html_re.js b/lib/common/html_re.js index 2d33f68..4fd9369 100644 --- a/lib/common/html_re.js +++ b/lib/common/html_re.js @@ -22,5 +22,7 @@ var cdata = ''; var HTML_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + '|' + comment + '|' + processing + '|' + declaration + '|' + cdata + ')'); +var HTML_OPEN_CLOSE_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + ')'); module.exports.HTML_TAG_RE = HTML_TAG_RE; +module.exports.HTML_OPEN_CLOSE_TAG_RE = HTML_OPEN_CLOSE_TAG_RE; diff --git a/lib/rules_block/html_block.js b/lib/rules_block/html_block.js index cc69596..0d9426b 100644 --- a/lib/rules_block/html_block.js +++ b/lib/rules_block/html_block.js @@ -4,69 +4,68 @@ 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)(?=(\s|>|$))/i, /<\/(script|pre|style)>/i, true ], + [ /^/, true ], + [ /^<\?/, /\?>/, true ], + [ /^/, true ], + [ /^/, true ], + [ new RegExp('^|$))', 'i'), /^$/, true ], + [ new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\s*$'), /^$/, false ] +]; -var HTML_TAG_OPEN_RE = /^<([a-zA-Z][a-zA-Z0-9]{0,14})[\s\/>]/; -var HTML_TAG_CLOSE_RE = /^<\/([a-zA-Z][a-zA-Z0-9]{0,14})[\s>]/; - -function isLetter(ch) { - /*eslint no-bitwise:0*/ - var lc = ch | 0x20; // to lower case - return (lc >= 0x61/* a */) && (lc <= 0x7a/* z */); -} - module.exports = function html_block(state, startLine, endLine, silent) { - var ch, match, nextLine, token, - pos = state.bMarks[startLine], - max = state.eMarks[startLine], - shift = state.tShift[startLine]; - - pos += shift; + var i, nextLine, token, lineText, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; if (!state.md.options.html) { return false; } - if (shift > 3 || pos + 2 >= max) { return false; } - if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; } - ch = state.src.charCodeAt(pos + 1); + lineText = state.src.slice(pos, max); - if (ch === 0x21/* ! */ || ch === 0x3F/* ? */) { - // Directive start / comment start / processing instruction start - if (silent) { return true; } + for (i = 0; i < HTML_SEQUENCES.length; i++) { + if (HTML_SEQUENCES[i][0].test(lineText)) { break; } + } - } else if (ch === 0x2F/* / */ || isLetter(ch)) { + if (i === HTML_SEQUENCES.length) { return false; } - // Probably start or end of tag - if (ch === 0x2F/* \ */) { - // closing tag - match = state.src.slice(pos, max).match(HTML_TAG_CLOSE_RE); - if (!match) { return false; } - } else { - // opening tag - match = state.src.slice(pos, max).match(HTML_TAG_OPEN_RE); - if (!match) { return false; } - } - // Make sure tag name is valid - if (block_names[match[1].toLowerCase()] !== true) { return false; } - if (silent) { return true; } - - } else { - return false; + if (silent) { + // true if this sequence can be a terminator, false otherwise + return HTML_SEQUENCES[i][2]; } - // If we are here - we detected HTML block. - // Let's roll down till empty line (block end). nextLine = startLine + 1; - while (nextLine < state.lineMax && !state.isEmpty(nextLine)) { - nextLine++; + + // 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.tShift[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, state.line ]; - token.content = state.getLines(startLine, nextLine, 0, true); + token.map = [ startLine, nextLine ]; + token.content = state.getLines(startLine, nextLine, state.blkIndent, true); return true; };