| 'use strict' |
| |
| var asciiAlpha = require('../character/ascii-alpha.js') |
| var asciiAlphanumeric = require('../character/ascii-alphanumeric.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 fromCharCode = require('../constant/from-char-code.js') |
| var htmlBlockNames = require('../constant/html-block-names.js') |
| var htmlRawNames = require('../constant/html-raw-names.js') |
| var partialBlankLine = require('./partial-blank-line.js') |
| |
| 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 === 'htmlFlow') { |
| break |
| } |
| } |
| |
| if (index > 1 && events[index - 2][1].type === '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) { |
| effects.enter('htmlFlow') |
| effects.enter('htmlFlowData') |
| effects.consume(code) |
| return open |
| } |
| |
| function open(code) { |
| if (code === 33) { |
| effects.consume(code) |
| return declarationStart |
| } |
| |
| if (code === 47) { |
| effects.consume(code) |
| return tagCloseStart |
| } |
| |
| if (code === 63) { |
| effects.consume(code) |
| kind = 3 // 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 === 45) { |
| effects.consume(code) |
| kind = 2 |
| return commentOpenInside |
| } |
| |
| if (code === 91) { |
| effects.consume(code) |
| kind = 5 |
| buffer = 'CDATA[' |
| index = 0 |
| return cdataOpenInside |
| } |
| |
| if (asciiAlpha(code)) { |
| effects.consume(code) |
| kind = 4 |
| return self.interrupt ? ok : continuationDeclarationInside |
| } |
| |
| return nok(code) |
| } |
| |
| function commentOpenInside(code) { |
| if (code === 45) { |
| 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 === null || |
| code === 47 || |
| code === 62 || |
| markdownLineEndingOrSpace(code) |
| ) { |
| if ( |
| code !== 47 && |
| startTag && |
| htmlRawNames.indexOf(buffer.toLowerCase()) > -1 |
| ) { |
| kind = 1 |
| return self.interrupt ? ok(code) : continuation(code) |
| } |
| |
| if (htmlBlockNames.indexOf(buffer.toLowerCase()) > -1) { |
| kind = 6 |
| |
| if (code === 47) { |
| effects.consume(code) |
| return basicSelfClosing |
| } |
| |
| return self.interrupt ? ok(code) : continuation(code) |
| } |
| |
| kind = 7 // Do not support complete HTML when interrupting. |
| |
| return self.interrupt |
| ? nok(code) |
| : startTag |
| ? completeAttributeNameBefore(code) |
| : completeClosingTagAfter(code) |
| } |
| |
| if (code === 45 || asciiAlphanumeric(code)) { |
| effects.consume(code) |
| buffer += fromCharCode(code) |
| return tagName |
| } |
| |
| return nok(code) |
| } |
| |
| function basicSelfClosing(code) { |
| if (code === 62) { |
| 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 === 47) { |
| effects.consume(code) |
| return completeEnd |
| } |
| |
| if (code === 58 || code === 95 || asciiAlpha(code)) { |
| effects.consume(code) |
| return completeAttributeName |
| } |
| |
| if (markdownSpace(code)) { |
| effects.consume(code) |
| return completeAttributeNameBefore |
| } |
| |
| return completeEnd(code) |
| } |
| |
| function completeAttributeName(code) { |
| if ( |
| code === 45 || |
| code === 46 || |
| code === 58 || |
| code === 95 || |
| asciiAlphanumeric(code) |
| ) { |
| effects.consume(code) |
| return completeAttributeName |
| } |
| |
| return completeAttributeNameAfter(code) |
| } |
| |
| function completeAttributeNameAfter(code) { |
| if (code === 61) { |
| effects.consume(code) |
| return completeAttributeValueBefore |
| } |
| |
| if (markdownSpace(code)) { |
| effects.consume(code) |
| return completeAttributeNameAfter |
| } |
| |
| return completeAttributeNameBefore(code) |
| } |
| |
| function completeAttributeValueBefore(code) { |
| if ( |
| code === null || |
| code === 60 || |
| code === 61 || |
| code === 62 || |
| code === 96 |
| ) { |
| return nok(code) |
| } |
| |
| if (code === 34 || code === 39) { |
| 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 === null || markdownLineEnding(code)) { |
| return nok(code) |
| } |
| |
| effects.consume(code) |
| return completeAttributeValueQuoted |
| } |
| |
| function completeAttributeValueUnquoted(code) { |
| if ( |
| code === null || |
| code === 34 || |
| code === 39 || |
| code === 60 || |
| code === 61 || |
| code === 62 || |
| code === 96 || |
| markdownLineEndingOrSpace(code) |
| ) { |
| return completeAttributeNameAfter(code) |
| } |
| |
| effects.consume(code) |
| return completeAttributeValueUnquoted |
| } |
| |
| function completeAttributeValueQuotedAfter(code) { |
| if (code === 47 || code === 62 || markdownSpace(code)) { |
| return completeAttributeNameBefore(code) |
| } |
| |
| return nok(code) |
| } |
| |
| function completeEnd(code) { |
| if (code === 62) { |
| effects.consume(code) |
| return completeAfter |
| } |
| |
| return nok(code) |
| } |
| |
| function completeAfter(code) { |
| if (markdownSpace(code)) { |
| effects.consume(code) |
| return completeAfter |
| } |
| |
| return code === null || markdownLineEnding(code) |
| ? continuation(code) |
| : nok(code) |
| } |
| |
| function continuation(code) { |
| if (code === 45 && kind === 2) { |
| effects.consume(code) |
| return continuationCommentInside |
| } |
| |
| if (code === 60 && kind === 1) { |
| effects.consume(code) |
| return continuationRawTagOpen |
| } |
| |
| if (code === 62 && kind === 4) { |
| effects.consume(code) |
| return continuationClose |
| } |
| |
| if (code === 63 && kind === 3) { |
| effects.consume(code) |
| return continuationDeclarationInside |
| } |
| |
| if (code === 93 && kind === 5) { |
| effects.consume(code) |
| return continuationCharacterDataInside |
| } |
| |
| if (markdownLineEnding(code) && (kind === 6 || kind === 7)) { |
| return effects.check( |
| nextBlankConstruct, |
| continuationClose, |
| continuationAtLineEnding |
| )(code) |
| } |
| |
| if (code === null || markdownLineEnding(code)) { |
| return continuationAtLineEnding(code) |
| } |
| |
| effects.consume(code) |
| return continuation |
| } |
| |
| function continuationAtLineEnding(code) { |
| effects.exit('htmlFlowData') |
| return htmlContinueStart(code) |
| } |
| |
| function htmlContinueStart(code) { |
| if (code === null) { |
| return done(code) |
| } |
| |
| if (markdownLineEnding(code)) { |
| effects.enter('lineEnding') |
| effects.consume(code) |
| effects.exit('lineEnding') |
| return htmlContinueStart |
| } |
| |
| effects.enter('htmlFlowData') |
| return continuation(code) |
| } |
| |
| function continuationCommentInside(code) { |
| if (code === 45) { |
| effects.consume(code) |
| return continuationDeclarationInside |
| } |
| |
| return continuation(code) |
| } |
| |
| function continuationRawTagOpen(code) { |
| if (code === 47) { |
| effects.consume(code) |
| buffer = '' |
| return continuationRawEndTag |
| } |
| |
| return continuation(code) |
| } |
| |
| function continuationRawEndTag(code) { |
| if (code === 62 && htmlRawNames.indexOf(buffer.toLowerCase()) > -1) { |
| effects.consume(code) |
| return continuationClose |
| } |
| |
| if (asciiAlpha(code) && buffer.length < 8) { |
| effects.consume(code) |
| buffer += fromCharCode(code) |
| return continuationRawEndTag |
| } |
| |
| return continuation(code) |
| } |
| |
| function continuationCharacterDataInside(code) { |
| if (code === 93) { |
| effects.consume(code) |
| return continuationDeclarationInside |
| } |
| |
| return continuation(code) |
| } |
| |
| function continuationDeclarationInside(code) { |
| if (code === 62) { |
| effects.consume(code) |
| return continuationClose |
| } |
| |
| return continuation(code) |
| } |
| |
| function continuationClose(code) { |
| if (code === null || markdownLineEnding(code)) { |
| effects.exit('htmlFlowData') |
| return done(code) |
| } |
| |
| effects.consume(code) |
| return continuationClose |
| } |
| |
| function done(code) { |
| effects.exit('htmlFlow') |
| return ok(code) |
| } |
| } |
| |
| function tokenizeNextBlank(effects, ok, nok) { |
| return start |
| |
| function start(code) { |
| effects.exit('htmlFlowData') |
| effects.enter('lineEndingBlank') |
| effects.consume(code) |
| effects.exit('lineEndingBlank') |
| return effects.attempt(partialBlankLine, ok, nok) |
| } |
| } |
| |
| module.exports = htmlFlow |