var htmlFlow = {
name: 'htmlFlow',
tokenize: tokenizeHtmlFlow,
resolveTo: resolveToHtmlFlow,
concrete: true
}
export default htmlFlow
import assert from 'assert'
import asciiAlpha from '../character/ascii-alpha.mjs'
import asciiAlphanumeric from '../character/ascii-alphanumeric.mjs'
import codes from '../character/codes.mjs'
import markdownLineEnding from '../character/markdown-line-ending.mjs'
import markdownLineEndingOrSpace from '../character/markdown-line-ending-or-space.mjs'
import markdownSpace from '../character/markdown-space.mjs'
import constants from '../constant/constants.mjs'
import fromCharCode from '../constant/from-char-code.mjs'
import basics from '../constant/html-block-names.mjs'
import raws from '../constant/html-raw-names.mjs'
import types from '../constant/types.mjs'
import blank from './partial-blank-line.mjs'
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(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 &&
raws.indexOf(buffer.toLowerCase()) > -1
) {
kind = constants.htmlRaw
return self.interrupt ? ok(code) : continuation(code)
}
if (basics.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 && raws.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(markdownLineEnding(code), 'expected a line ending')
effects.exit(types.htmlFlowData)
effects.enter(types.lineEndingBlank)
effects.consume(code)
effects.exit(types.lineEndingBlank)
return effects.attempt(blank, ok, nok)
}
}