You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
513 lines
12 KiB
513 lines
12 KiB
'use strict' |
|
|
|
var assert = require('assert') |
|
var asciiAlpha = require('../character/ascii-alpha.js') |
|
var asciiAlphanumeric = require('../character/ascii-alphanumeric.js') |
|
var codes = require('../character/codes.js') |
|
var markdownLineEnding = require('../character/markdown-line-ending.js') |
|
var markdownLineEndingOrSpace = require('../character/markdown-line-ending-or-space.js') |
|
var markdownSpace = require('../character/markdown-space.js') |
|
var constants = require('../constant/constants.js') |
|
var fromCharCode = require('../constant/from-char-code.js') |
|
var htmlBlockNames = require('../constant/html-block-names.js') |
|
var htmlRawNames = require('../constant/html-raw-names.js') |
|
var types = require('../constant/types.js') |
|
var partialBlankLine = require('./partial-blank-line.js') |
|
|
|
function _interopDefaultLegacy(e) { |
|
return e && typeof e === 'object' && 'default' in e ? e : {default: e} |
|
} |
|
|
|
var assert__default = /*#__PURE__*/ _interopDefaultLegacy(assert) |
|
|
|
var htmlFlow = { |
|
name: 'htmlFlow', |
|
tokenize: tokenizeHtmlFlow, |
|
resolveTo: resolveToHtmlFlow, |
|
concrete: true |
|
} |
|
|
|
var nextBlankConstruct = {tokenize: tokenizeNextBlank, partial: true} |
|
|
|
function resolveToHtmlFlow(events) { |
|
var index = events.length |
|
|
|
while (index--) { |
|
if ( |
|
events[index][0] === 'enter' && |
|
events[index][1].type === types.htmlFlow |
|
) { |
|
break |
|
} |
|
} |
|
|
|
if (index > 1 && events[index - 2][1].type === types.linePrefix) { |
|
// Add the prefix start to the HTML token. |
|
events[index][1].start = events[index - 2][1].start |
|
// Add the prefix start to the HTML line token. |
|
events[index + 1][1].start = events[index - 2][1].start |
|
// Remove the line prefix. |
|
events.splice(index - 2, 2) |
|
} |
|
|
|
return events |
|
} |
|
|
|
function tokenizeHtmlFlow(effects, ok, nok) { |
|
var self = this |
|
var kind |
|
var startTag |
|
var buffer |
|
var index |
|
var marker |
|
|
|
return start |
|
|
|
function start(code) { |
|
assert__default['default'](code === codes.lessThan, 'expected `<`') |
|
effects.enter(types.htmlFlow) |
|
effects.enter(types.htmlFlowData) |
|
effects.consume(code) |
|
return open |
|
} |
|
|
|
function open(code) { |
|
if (code === codes.exclamationMark) { |
|
effects.consume(code) |
|
return declarationStart |
|
} |
|
|
|
if (code === codes.slash) { |
|
effects.consume(code) |
|
return tagCloseStart |
|
} |
|
|
|
if (code === codes.questionMark) { |
|
effects.consume(code) |
|
kind = constants.htmlInstruction |
|
// While we’re in an instruction instead of a declaration, we’re on a `?` |
|
// right now, so we do need to search for `>`, similar to declarations. |
|
return self.interrupt ? ok : continuationDeclarationInside |
|
} |
|
|
|
if (asciiAlpha(code)) { |
|
effects.consume(code) |
|
buffer = fromCharCode(code) |
|
startTag = true |
|
return tagName |
|
} |
|
|
|
return nok(code) |
|
} |
|
|
|
function declarationStart(code) { |
|
if (code === codes.dash) { |
|
effects.consume(code) |
|
kind = constants.htmlComment |
|
return commentOpenInside |
|
} |
|
|
|
if (code === codes.leftSquareBracket) { |
|
effects.consume(code) |
|
kind = constants.htmlCdata |
|
buffer = constants.cdataOpeningString |
|
index = 0 |
|
return cdataOpenInside |
|
} |
|
|
|
if (asciiAlpha(code)) { |
|
effects.consume(code) |
|
kind = constants.htmlDeclaration |
|
return self.interrupt ? ok : continuationDeclarationInside |
|
} |
|
|
|
return nok(code) |
|
} |
|
|
|
function commentOpenInside(code) { |
|
if (code === codes.dash) { |
|
effects.consume(code) |
|
return self.interrupt ? ok : continuationDeclarationInside |
|
} |
|
|
|
return nok(code) |
|
} |
|
|
|
function cdataOpenInside(code) { |
|
if (code === buffer.charCodeAt(index++)) { |
|
effects.consume(code) |
|
return index === buffer.length |
|
? self.interrupt |
|
? ok |
|
: continuation |
|
: cdataOpenInside |
|
} |
|
|
|
return nok(code) |
|
} |
|
|
|
function tagCloseStart(code) { |
|
if (asciiAlpha(code)) { |
|
effects.consume(code) |
|
buffer = fromCharCode(code) |
|
return tagName |
|
} |
|
|
|
return nok(code) |
|
} |
|
|
|
function tagName(code) { |
|
if ( |
|
code === codes.eof || |
|
code === codes.slash || |
|
code === codes.greaterThan || |
|
markdownLineEndingOrSpace(code) |
|
) { |
|
if ( |
|
code !== codes.slash && |
|
startTag && |
|
htmlRawNames.indexOf(buffer.toLowerCase()) > -1 |
|
) { |
|
kind = constants.htmlRaw |
|
return self.interrupt ? ok(code) : continuation(code) |
|
} |
|
|
|
if (htmlBlockNames.indexOf(buffer.toLowerCase()) > -1) { |
|
kind = constants.htmlBasic |
|
|
|
if (code === codes.slash) { |
|
effects.consume(code) |
|
return basicSelfClosing |
|
} |
|
|
|
return self.interrupt ? ok(code) : continuation(code) |
|
} |
|
|
|
kind = constants.htmlComplete |
|
// Do not support complete HTML when interrupting. |
|
return self.interrupt |
|
? nok(code) |
|
: startTag |
|
? completeAttributeNameBefore(code) |
|
: completeClosingTagAfter(code) |
|
} |
|
|
|
if (code === codes.dash || asciiAlphanumeric(code)) { |
|
effects.consume(code) |
|
buffer += fromCharCode(code) |
|
return tagName |
|
} |
|
|
|
return nok(code) |
|
} |
|
|
|
function basicSelfClosing(code) { |
|
if (code === codes.greaterThan) { |
|
effects.consume(code) |
|
return self.interrupt ? ok : continuation |
|
} |
|
|
|
return nok(code) |
|
} |
|
|
|
function completeClosingTagAfter(code) { |
|
if (markdownSpace(code)) { |
|
effects.consume(code) |
|
return completeClosingTagAfter |
|
} |
|
|
|
return completeEnd(code) |
|
} |
|
|
|
function completeAttributeNameBefore(code) { |
|
if (code === codes.slash) { |
|
effects.consume(code) |
|
return completeEnd |
|
} |
|
|
|
if (code === codes.colon || code === codes.underscore || asciiAlpha(code)) { |
|
effects.consume(code) |
|
return completeAttributeName |
|
} |
|
|
|
if (markdownSpace(code)) { |
|
effects.consume(code) |
|
return completeAttributeNameBefore |
|
} |
|
|
|
return completeEnd(code) |
|
} |
|
|
|
function completeAttributeName(code) { |
|
if ( |
|
code === codes.dash || |
|
code === codes.dot || |
|
code === codes.colon || |
|
code === codes.underscore || |
|
asciiAlphanumeric(code) |
|
) { |
|
effects.consume(code) |
|
return completeAttributeName |
|
} |
|
|
|
return completeAttributeNameAfter(code) |
|
} |
|
|
|
function completeAttributeNameAfter(code) { |
|
if (code === codes.equalsTo) { |
|
effects.consume(code) |
|
return completeAttributeValueBefore |
|
} |
|
|
|
if (markdownSpace(code)) { |
|
effects.consume(code) |
|
return completeAttributeNameAfter |
|
} |
|
|
|
return completeAttributeNameBefore(code) |
|
} |
|
|
|
function completeAttributeValueBefore(code) { |
|
if ( |
|
code === codes.eof || |
|
code === codes.lessThan || |
|
code === codes.equalsTo || |
|
code === codes.greaterThan || |
|
code === codes.graveAccent |
|
) { |
|
return nok(code) |
|
} |
|
|
|
if (code === codes.quotationMark || code === codes.apostrophe) { |
|
effects.consume(code) |
|
marker = code |
|
return completeAttributeValueQuoted |
|
} |
|
|
|
if (markdownSpace(code)) { |
|
effects.consume(code) |
|
return completeAttributeValueBefore |
|
} |
|
|
|
marker = undefined |
|
return completeAttributeValueUnquoted(code) |
|
} |
|
|
|
function completeAttributeValueQuoted(code) { |
|
if (code === marker) { |
|
effects.consume(code) |
|
return completeAttributeValueQuotedAfter |
|
} |
|
|
|
if (code === codes.eof || markdownLineEnding(code)) { |
|
return nok(code) |
|
} |
|
|
|
effects.consume(code) |
|
return completeAttributeValueQuoted |
|
} |
|
|
|
function completeAttributeValueUnquoted(code) { |
|
if ( |
|
code === codes.eof || |
|
code === codes.quotationMark || |
|
code === codes.apostrophe || |
|
code === codes.lessThan || |
|
code === codes.equalsTo || |
|
code === codes.greaterThan || |
|
code === codes.graveAccent || |
|
markdownLineEndingOrSpace(code) |
|
) { |
|
return completeAttributeNameAfter(code) |
|
} |
|
|
|
effects.consume(code) |
|
return completeAttributeValueUnquoted |
|
} |
|
|
|
function completeAttributeValueQuotedAfter(code) { |
|
if ( |
|
code === codes.slash || |
|
code === codes.greaterThan || |
|
markdownSpace(code) |
|
) { |
|
return completeAttributeNameBefore(code) |
|
} |
|
|
|
return nok(code) |
|
} |
|
|
|
function completeEnd(code) { |
|
if (code === codes.greaterThan) { |
|
effects.consume(code) |
|
return completeAfter |
|
} |
|
|
|
return nok(code) |
|
} |
|
|
|
function completeAfter(code) { |
|
if (markdownSpace(code)) { |
|
effects.consume(code) |
|
return completeAfter |
|
} |
|
|
|
return code === codes.eof || markdownLineEnding(code) |
|
? continuation(code) |
|
: nok(code) |
|
} |
|
|
|
function continuation(code) { |
|
if (code === codes.dash && kind === constants.htmlComment) { |
|
effects.consume(code) |
|
return continuationCommentInside |
|
} |
|
|
|
if (code === codes.lessThan && kind === constants.htmlRaw) { |
|
effects.consume(code) |
|
return continuationRawTagOpen |
|
} |
|
|
|
if (code === codes.greaterThan && kind === constants.htmlDeclaration) { |
|
effects.consume(code) |
|
return continuationClose |
|
} |
|
|
|
if (code === codes.questionMark && kind === constants.htmlInstruction) { |
|
effects.consume(code) |
|
return continuationDeclarationInside |
|
} |
|
|
|
if (code === codes.rightSquareBracket && kind === constants.htmlCdata) { |
|
effects.consume(code) |
|
return continuationCharacterDataInside |
|
} |
|
|
|
if ( |
|
markdownLineEnding(code) && |
|
(kind === constants.htmlBasic || kind === constants.htmlComplete) |
|
) { |
|
return effects.check( |
|
nextBlankConstruct, |
|
continuationClose, |
|
continuationAtLineEnding |
|
)(code) |
|
} |
|
|
|
if (code === codes.eof || markdownLineEnding(code)) { |
|
return continuationAtLineEnding(code) |
|
} |
|
|
|
effects.consume(code) |
|
return continuation |
|
} |
|
|
|
function continuationAtLineEnding(code) { |
|
effects.exit(types.htmlFlowData) |
|
return htmlContinueStart(code) |
|
} |
|
|
|
function htmlContinueStart(code) { |
|
if (code === codes.eof) { |
|
return done(code) |
|
} |
|
|
|
if (markdownLineEnding(code)) { |
|
effects.enter(types.lineEnding) |
|
effects.consume(code) |
|
effects.exit(types.lineEnding) |
|
return htmlContinueStart |
|
} |
|
|
|
effects.enter(types.htmlFlowData) |
|
return continuation(code) |
|
} |
|
|
|
function continuationCommentInside(code) { |
|
if (code === codes.dash) { |
|
effects.consume(code) |
|
return continuationDeclarationInside |
|
} |
|
|
|
return continuation(code) |
|
} |
|
|
|
function continuationRawTagOpen(code) { |
|
if (code === codes.slash) { |
|
effects.consume(code) |
|
buffer = '' |
|
return continuationRawEndTag |
|
} |
|
|
|
return continuation(code) |
|
} |
|
|
|
function continuationRawEndTag(code) { |
|
if ( |
|
code === codes.greaterThan && |
|
htmlRawNames.indexOf(buffer.toLowerCase()) > -1 |
|
) { |
|
effects.consume(code) |
|
return continuationClose |
|
} |
|
|
|
if (asciiAlpha(code) && buffer.length < constants.htmlRawSizeMax) { |
|
effects.consume(code) |
|
buffer += fromCharCode(code) |
|
return continuationRawEndTag |
|
} |
|
|
|
return continuation(code) |
|
} |
|
|
|
function continuationCharacterDataInside(code) { |
|
if (code === codes.rightSquareBracket) { |
|
effects.consume(code) |
|
return continuationDeclarationInside |
|
} |
|
|
|
return continuation(code) |
|
} |
|
|
|
function continuationDeclarationInside(code) { |
|
if (code === codes.greaterThan) { |
|
effects.consume(code) |
|
return continuationClose |
|
} |
|
|
|
return continuation(code) |
|
} |
|
|
|
function continuationClose(code) { |
|
if (code === codes.eof || markdownLineEnding(code)) { |
|
effects.exit(types.htmlFlowData) |
|
return done(code) |
|
} |
|
|
|
effects.consume(code) |
|
return continuationClose |
|
} |
|
|
|
function done(code) { |
|
effects.exit(types.htmlFlow) |
|
return ok(code) |
|
} |
|
} |
|
|
|
function tokenizeNextBlank(effects, ok, nok) { |
|
return start |
|
|
|
function start(code) { |
|
assert__default['default']( |
|
markdownLineEnding(code), |
|
'expected a line ending' |
|
) |
|
effects.exit(types.htmlFlowData) |
|
effects.enter(types.lineEndingBlank) |
|
effects.consume(code) |
|
effects.exit(types.lineEndingBlank) |
|
return effects.attempt(partialBlankLine, ok, nok) |
|
} |
|
} |
|
|
|
module.exports = htmlFlow
|
|
|