"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = parser; exports.hasTerminalParent = hasTerminalParent; exports.parse = parse; exports.rewindStack = rewindStack; var _compat = require("./compat"); function parser(tokens, options) { var root = { tagName: null, children: [] }; var state = { tokens: tokens, options: options, cursor: 0, stack: [root] }; parse(state); return root.children; } function hasTerminalParent(tagName, stack, terminals) { var tagParents = terminals[tagName]; if (tagParents) { var currentIndex = stack.length - 1; while (currentIndex >= 0) { var parentTagName = stack[currentIndex].tagName; if (parentTagName === tagName) { break; } if ((0, _compat.arrayIncludes)(tagParents, parentTagName)) { return true; } currentIndex--; } } return false; } function rewindStack(stack, newLength, childrenEndPosition, endPosition) { stack[newLength].position.end = endPosition; for (var i = newLength + 1, len = stack.length; i < len; i++) { stack[i].position.end = childrenEndPosition; } stack.splice(newLength); } function parse(state) { var tokens = state.tokens, options = state.options; var stack = state.stack; var nodes = stack[stack.length - 1].children; var len = tokens.length; var cursor = state.cursor; while (cursor < len) { var token = tokens[cursor]; if (token.type !== 'tag-start') { nodes.push(token); cursor++; continue; } var tagToken = tokens[++cursor]; cursor++; var tagName = tagToken.content.toLowerCase(); if (token.close) { var index = stack.length; var shouldRewind = false; while (--index > -1) { if (stack[index].tagName === tagName) { shouldRewind = true; break; } } while (cursor < len) { var endToken = tokens[cursor]; if (endToken.type !== 'tag-end') break; cursor++; } if (shouldRewind) { rewindStack(stack, index, token.position.start, tokens[cursor - 1].position.end); break; } else { continue; } } var isClosingTag = (0, _compat.arrayIncludes)(options.closingTags, tagName); var shouldRewindToAutoClose = isClosingTag; if (shouldRewindToAutoClose) { var terminals = options.closingTagAncestorBreakers; shouldRewindToAutoClose = !hasTerminalParent(tagName, stack, terminals); } if (shouldRewindToAutoClose) { // rewind the stack to just above the previous // closing tag of the same name var currentIndex = stack.length - 1; while (currentIndex > 0) { if (tagName === stack[currentIndex].tagName) { rewindStack(stack, currentIndex, token.position.start, token.position.start); var previousIndex = currentIndex - 1; nodes = stack[previousIndex].children; break; } currentIndex = currentIndex - 1; } } var attributes = []; var attrToken = void 0; while (cursor < len) { attrToken = tokens[cursor]; if (attrToken.type === 'tag-end') break; attributes.push(attrToken.content); cursor++; } cursor++; var children = []; var position = { start: token.position.start, end: attrToken.position.end }; var elementNode = { type: 'element', tagName: tagToken.content, attributes: attributes, children: children, position: position }; nodes.push(elementNode); var hasChildren = !(attrToken.close || (0, _compat.arrayIncludes)(options.voidTags, tagName)); if (hasChildren) { var size = stack.push({ tagName: tagName, children: children, position: position }); var innerState = { tokens: tokens, options: options, cursor: cursor, stack: stack }; parse(innerState); cursor = innerState.cursor; var rewoundInElement = stack.length === size; if (rewoundInElement) { elementNode.position.end = tokens[cursor - 1].position.end; } } } state.cursor = cursor; } //# sourceMappingURL=parser.js.map