UNPKG

@bbob/parser

Version:

Just parses BBcode to AST array. Part of @bbob bbcode parser

1,424 lines (1,129 loc) 33.6 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.BbobParser = {})); }(this, (function (exports) { 'use strict'; function unwrapExports (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var char_1 = createCommonjsModule(function (module, exports) { exports.__esModule = true; exports.BACKSLASH = exports.PLACEHOLDER_SPACE = exports.PLACEHOLDER_SPACE_TAB = exports.SLASH = exports.CLOSE_BRAKET = exports.OPEN_BRAKET = exports.SPACE = exports.QUOTEMARK = exports.EQ = exports.TAB = exports.R = exports.F = exports.N = void 0; var N = '\n'; exports.N = N; var TAB = '\t'; exports.TAB = TAB; var F = '\f'; exports.F = F; var R = '\r'; exports.R = R; var EQ = '='; exports.EQ = EQ; var QUOTEMARK = '"'; exports.QUOTEMARK = QUOTEMARK; var SPACE = ' '; exports.SPACE = SPACE; var OPEN_BRAKET = '['; exports.OPEN_BRAKET = OPEN_BRAKET; var CLOSE_BRAKET = ']'; exports.CLOSE_BRAKET = CLOSE_BRAKET; var SLASH = '/'; exports.SLASH = SLASH; var BACKSLASH = '\\'; exports.BACKSLASH = BACKSLASH; var PLACEHOLDER_SPACE_TAB = ' '; exports.PLACEHOLDER_SPACE_TAB = PLACEHOLDER_SPACE_TAB; var PLACEHOLDER_SPACE = ' '; // const getChar = String.fromCharCode; exports.PLACEHOLDER_SPACE = PLACEHOLDER_SPACE; }); unwrapExports(char_1); var char_2 = char_1.BACKSLASH; var char_3 = char_1.PLACEHOLDER_SPACE; var char_4 = char_1.PLACEHOLDER_SPACE_TAB; var char_5 = char_1.SLASH; var char_6 = char_1.CLOSE_BRAKET; var char_7 = char_1.OPEN_BRAKET; var char_8 = char_1.SPACE; var char_9 = char_1.QUOTEMARK; var char_10 = char_1.EQ; var char_11 = char_1.TAB; var char_12 = char_1.R; var char_13 = char_1.F; var char_14 = char_1.N; var lib = createCommonjsModule(function (module, exports) { exports.__esModule = true; exports.isEOL = exports.isStringNode = exports.isTagNode = exports.getUniqAttr = exports.getNodeLength = exports.escapeHTML = exports.appendToNode = exports.attrValue = exports.attrsToString = void 0; var isTagNode = function isTagNode(el) { return typeof el === 'object' && !!el.tag; }; exports.isTagNode = isTagNode; var isStringNode = function isStringNode(el) { return typeof el === 'string'; }; exports.isStringNode = isStringNode; var isEOL = function isEOL(el) { return el === char_1.N; }; exports.isEOL = isEOL; var keysReduce = function keysReduce(obj, reduce, def) { return Object.keys(obj).reduce(reduce, def); }; var getNodeLength = function getNodeLength(node) { if (isTagNode(node)) { return node.content.reduce(function (count, contentNode) { return count + getNodeLength(contentNode); }, 0); } if (isStringNode(node)) { return node.length; } return 0; }; /** * Appends value to Tag Node * @param {TagNode} node * @param value */ exports.getNodeLength = getNodeLength; var appendToNode = function appendToNode(node, value) { node.content.push(value); }; /** * Replaces " to &qquot; * @param {String} value */ exports.appendToNode = appendToNode; var escapeHTML = function escapeHTML(value) { return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;') // eslint-disable-next-line no-script-url .replace(/(javascript):/gi, '$1%3A'); }; /** * Acept name and value and return valid html5 attribute string * @param {String} name * @param {String} value * @return {string} */ exports.escapeHTML = escapeHTML; var attrValue = function attrValue(name, value) { var type = typeof value; var types = { "boolean": function boolean() { return value ? "" + name : ''; }, number: function number() { return name + "=\"" + value + "\""; }, string: function string() { return name + "=\"" + escapeHTML(value) + "\""; }, object: function object() { return name + "=\"" + escapeHTML(JSON.stringify(value)) + "\""; } }; return types[type] ? types[type]() : ''; }; /** * Transforms attrs to html params string * @param values */ exports.attrValue = attrValue; var attrsToString = function attrsToString(values) { // To avoid some malformed attributes if (values == null) { return ''; } return keysReduce(values, function (arr, key) { return [].concat(arr, [attrValue(key, values[key])]); }, ['']).join(' '); }; /** * Gets value from * @example * getUniqAttr({ 'foo': true, 'bar': bar' }) => 'bar' * @param attrs * @returns {string} */ exports.attrsToString = attrsToString; var getUniqAttr = function getUniqAttr(attrs) { return keysReduce(attrs, function (res, key) { return attrs[key] === key ? attrs[key] : null; }, null); }; exports.getUniqAttr = getUniqAttr; }); unwrapExports(lib); var lib_1 = lib.isEOL; var lib_2 = lib.isStringNode; var lib_3 = lib.isTagNode; var lib_4 = lib.getUniqAttr; var lib_5 = lib.getNodeLength; var lib_6 = lib.escapeHTML; var lib_7 = lib.appendToNode; var lib_8 = lib.attrValue; var lib_9 = lib.attrsToString; var TagNode_1 = createCommonjsModule(function (module, exports) { exports.__esModule = true; exports["default"] = exports.TagNode = void 0; function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } var getTagAttrs = function getTagAttrs(tag, params) { var uniqAattr = (0, lib.getUniqAttr)(params); if (uniqAattr) { var tagAttr = (0, lib.attrValue)(tag, uniqAattr); var attrs = _extends({}, params); delete attrs[uniqAattr]; var attrsStr = (0, lib.attrsToString)(attrs); return "" + tagAttr + attrsStr; } return "" + tag + (0, lib.attrsToString)(params); }; var TagNode = /*#__PURE__*/ function () { function TagNode(tag, attrs, content) { this.tag = tag; this.attrs = attrs; this.content = Array.isArray(content) ? content : [content]; } var _proto = TagNode.prototype; _proto.attr = function attr(name, value) { if (typeof value !== 'undefined') { this.attrs[name] = value; } return this.attrs[name]; }; _proto.append = function append(value) { return (0, lib.appendToNode)(this, value); }; _proto.toTagStart = function toTagStart(_temp) { var _ref = _temp === void 0 ? {} : _temp, _ref$openTag = _ref.openTag, openTag = _ref$openTag === void 0 ? char_1.OPEN_BRAKET : _ref$openTag, _ref$closeTag = _ref.closeTag, closeTag = _ref$closeTag === void 0 ? char_1.CLOSE_BRAKET : _ref$closeTag; var tagAttrs = getTagAttrs(this.tag, this.attrs); return "" + openTag + tagAttrs + closeTag; }; _proto.toTagEnd = function toTagEnd(_temp2) { var _ref2 = _temp2 === void 0 ? {} : _temp2, _ref2$openTag = _ref2.openTag, openTag = _ref2$openTag === void 0 ? char_1.OPEN_BRAKET : _ref2$openTag, _ref2$closeTag = _ref2.closeTag, closeTag = _ref2$closeTag === void 0 ? char_1.CLOSE_BRAKET : _ref2$closeTag; return "" + openTag + char_1.SLASH + this.tag + closeTag; }; _proto.toTagNode = function toTagNode() { return new TagNode(this.tag.toLowerCase(), this.attrs, this.content); }; _proto.toString = function toString(_temp3) { var _ref3 = _temp3 === void 0 ? {} : _temp3, _ref3$openTag = _ref3.openTag, openTag = _ref3$openTag === void 0 ? char_1.OPEN_BRAKET : _ref3$openTag, _ref3$closeTag = _ref3.closeTag, closeTag = _ref3$closeTag === void 0 ? char_1.CLOSE_BRAKET : _ref3$closeTag; var isEmpty = this.content.length === 0; var content = this.content.reduce(function (r, node) { return r + node.toString({ openTag: openTag, closeTag: closeTag }); }, ''); var tagStart = this.toTagStart({ openTag: openTag, closeTag: closeTag }); if (isEmpty) { return tagStart; } return "" + tagStart + content + this.toTagEnd({ openTag: openTag, closeTag: closeTag }); }; _createClass(TagNode, [{ key: "length", get: function get() { return (0, lib.getNodeLength)(this); } }]); return TagNode; }(); exports.TagNode = TagNode; TagNode.create = function (tag, attrs, content) { if (attrs === void 0) { attrs = {}; } if (content === void 0) { content = []; } return new TagNode(tag, attrs, content); }; TagNode.isOf = function (node, type) { return node.tag === type; }; var _default = TagNode; exports["default"] = _default; }); var TagNode = unwrapExports(TagNode_1); var TagNode_2 = TagNode_1.TagNode; var TOKEN_TYPE_ID = 'type'; // 0; var TOKEN_VALUE_ID = 'value'; // 1; var TOKEN_COLUMN_ID = 'row'; // 2; var TOKEN_LINE_ID = 'line'; // 3; var TOKEN_TYPE_WORD = 1; // 'word'; var TOKEN_TYPE_TAG = 2; // 'tag'; var TOKEN_TYPE_ATTR_NAME = 3; // 'attr-name'; var TOKEN_TYPE_ATTR_VALUE = 4; // 'attr-value'; var TOKEN_TYPE_SPACE = 5; // 'space'; var TOKEN_TYPE_NEW_LINE = 6; // 'new-line'; /** * @param {Token} token * @returns {string} */ var getTokenValue = function getTokenValue(token) { if (token && typeof token[TOKEN_VALUE_ID] !== 'undefined') { return token[TOKEN_VALUE_ID]; } return ''; }; /** * @param {Token}token * @returns {number} */ var getTokenLine = function getTokenLine(token) { return token && token[TOKEN_LINE_ID] || 0; }; var getTokenColumn = function getTokenColumn(token) { return token && token[TOKEN_COLUMN_ID] || 0; }; /** * @param {Token} token * @returns {boolean} */ var isTextToken = function isTextToken(token) { if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { return token[TOKEN_TYPE_ID] === TOKEN_TYPE_SPACE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_NEW_LINE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_WORD; } return false; }; /** * @param {Token} token * @returns {boolean} */ var isTagToken = function isTagToken(token) { if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { return token[TOKEN_TYPE_ID] === TOKEN_TYPE_TAG; } return false; }; var isTagEnd = function isTagEnd(token) { return getTokenValue(token).charCodeAt(0) === char_5.charCodeAt(0); }; var isTagStart = function isTagStart(token) { return !isTagEnd(token); }; var isAttrNameToken = function isAttrNameToken(token) { if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_NAME; } return false; }; /** * @param {Token} token * @returns {boolean} */ var isAttrValueToken = function isAttrValueToken(token) { if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_VALUE; } return false; }; var getTagName = function getTagName(token) { var value = getTokenValue(token); return isTagEnd(token) ? value.slice(1) : value; }; var convertTagToText = function convertTagToText(token) { var text = char_7; text += getTokenValue(token); text += char_6; return text; }; var Token = /*#__PURE__*/ function () { /** * @param {String} type * @param {String} value * @param line * @param row */ function Token(type, value, line, row) { this[TOKEN_TYPE_ID] = Number(type); this[TOKEN_VALUE_ID] = String(value); this[TOKEN_LINE_ID] = Number(line); this[TOKEN_COLUMN_ID] = Number(row); } var _proto = Token.prototype; _proto.isEmpty = function isEmpty() { // eslint-disable-next-line no-restricted-globals return isNaN(this[TOKEN_TYPE_ID]); }; _proto.isText = function isText() { return isTextToken(this); }; _proto.isTag = function isTag() { return isTagToken(this); }; _proto.isAttrName = function isAttrName() { return isAttrNameToken(this); }; _proto.isAttrValue = function isAttrValue() { return isAttrValueToken(this); }; _proto.isStart = function isStart() { return isTagStart(this); }; _proto.isEnd = function isEnd() { return isTagEnd(this); }; _proto.getName = function getName() { return getTagName(this); }; _proto.getValue = function getValue() { return getTokenValue(this); }; _proto.getLine = function getLine() { return getTokenLine(this); }; _proto.getColumn = function getColumn() { return getTokenColumn(this); }; _proto.toString = function toString() { return convertTagToText(this); }; return Token; }(); var TYPE_WORD = TOKEN_TYPE_WORD; var TYPE_TAG = TOKEN_TYPE_TAG; var TYPE_ATTR_NAME = TOKEN_TYPE_ATTR_NAME; var TYPE_ATTR_VALUE = TOKEN_TYPE_ATTR_VALUE; var TYPE_SPACE = TOKEN_TYPE_SPACE; var TYPE_NEW_LINE = TOKEN_TYPE_NEW_LINE; function CharGrabber(source, options) { var cursor = { pos: 0, len: source.length }; var substrUntilChar = function substrUntilChar(char) { var pos = cursor.pos; var idx = source.indexOf(char, pos); return idx >= 0 ? source.substr(pos, idx - pos) : ''; }; var includes = function includes(val) { return source.indexOf(val, cursor.pos) >= 0; }; var hasNext = function hasNext() { return cursor.len > cursor.pos; }; var isLast = function isLast() { return cursor.pos === cursor.len; }; var skip = function skip(num, silent) { if (num === void 0) { num = 1; } cursor.pos += num; if (options && options.onSkip && !silent) { options.onSkip(); } }; var rest = function rest() { return source.substr(cursor.pos); }; var curr = function curr() { return source[cursor.pos]; }; var prev = function prev() { var prevPos = cursor.pos - 1; return typeof source[prevPos] !== 'undefined' ? source[prevPos] : null; }; var next = function next() { var nextPos = cursor.pos + 1; return nextPos <= source.length - 1 ? source[nextPos] : null; }; var grabWhile = function grabWhile(cond, silent) { var start = 0; if (hasNext()) { start = cursor.pos; while (hasNext() && cond(curr())) { skip(1, silent); } } return source.substr(start, cursor.pos - start); }; /** * @type {skip} */ this.skip = skip; /** * @returns {Boolean} */ this.hasNext = hasNext; /** * @returns {String} */ this.getCurr = curr; /** * @returns {String} */ this.getRest = rest; /** * @returns {String} */ this.getNext = next; /** * @returns {String} */ this.getPrev = prev; /** * @returns {Boolean} */ this.isLast = isLast; /** * @returns {Boolean} */ this.includes = includes; /** * @param {Function} cond * @param {Boolean} silent * @return {String} */ this.grabWhile = grabWhile; /** * Grabs rest of string until it find a char * @param {String} char * @return {String} */ this.substrUntilChar = substrUntilChar; } /** * Creates a grabber wrapper for source string, that helps to iterate over string char by char * @param {String} source * @param {Object} options * @param {Function} options.onSkip * @return CharGrabber */ var createCharGrabber = function createCharGrabber(source, options) { return new CharGrabber(source, options); }; /** * Trims string from start and end by char * @example * trimChar('*hello*', '*') ==> 'hello' * @param {String} str * @param {String} charToRemove * @returns {String} */ var trimChar = function trimChar(str, charToRemove) { while (str.charAt(0) === charToRemove) { // eslint-disable-next-line no-param-reassign str = str.substring(1); } while (str.charAt(str.length - 1) === charToRemove) { // eslint-disable-next-line no-param-reassign str = str.substring(0, str.length - 1); } return str; }; /** * Unquotes \" to " * @param str * @return {String} */ var unquote = function unquote(str) { return str.replace(char_2 + char_9, char_9); }; function NodeList(values) { if (values === void 0) { values = []; } var nodes = values; var getLast = function getLast() { return Array.isArray(nodes) && nodes.length > 0 && typeof nodes[nodes.length - 1] !== 'undefined' ? nodes[nodes.length - 1] : null; }; var flushLast = function flushLast() { return nodes.length ? nodes.pop() : false; }; var push = function push(value) { return nodes.push(value); }; var toArray = function toArray() { return nodes; }; this.push = push; this.toArray = toArray; this.getLast = getLast; this.flushLast = flushLast; } /** * * @param values * @return {NodeList} */ var createList = function createList(values) { if (values === void 0) { values = []; } return new NodeList(values); }; /* eslint-disable no-plusplus,no-param-reassign */ var EM = '!'; /** * Creates a Token entity class * @param {Number} type * @param {String} value * @param {Number} r line number * @param {Number} cl char number in line */ var createToken = function createToken(type, value, r, cl) { if (r === void 0) { r = 0; } if (cl === void 0) { cl = 0; } return new Token(type, value, r, cl); }; /** * @typedef {Object} Lexer * @property {Function} tokenize * @property {Function} isTokenNested */ /** * @param {String} buffer * @param {Object} options * @param {Function} options.onToken * @param {String} options.openTag * @param {String} options.closeTag * @param {Boolean} options.enableEscapeTags * @return {Lexer} */ function createLexer(buffer, options) { if (options === void 0) { options = {}; } var STATE_WORD = 0; var STATE_TAG = 1; var STATE_TAG_ATTRS = 2; var TAG_STATE_NAME = 0; var TAG_STATE_ATTR = 1; var TAG_STATE_VALUE = 2; var row = 0; var col = 0; var tokenIndex = -1; var stateMode = STATE_WORD; var tagMode = TAG_STATE_NAME; var tokens = new Array(Math.floor(buffer.length)); var openTag = options.openTag || char_7; var closeTag = options.closeTag || char_6; var escapeTags = !!options.enableEscapeTags; var onToken = options.onToken || function () {}; var RESERVED_CHARS = [closeTag, openTag, char_9, char_2, char_8, char_11, char_10, char_14, EM]; var NOT_CHAR_TOKENS = [// ...(options.enableEscapeTags ? [BACKSLASH] : []), openTag, char_8, char_11, char_14]; var WHITESPACES = [char_8, char_11]; var SPECIAL_CHARS = [char_10, char_8, char_11]; var isCharReserved = function isCharReserved(char) { return RESERVED_CHARS.indexOf(char) >= 0; }; var isNewLine = function isNewLine(char) { return char === char_14; }; var isWhiteSpace = function isWhiteSpace(char) { return WHITESPACES.indexOf(char) >= 0; }; var isCharToken = function isCharToken(char) { return NOT_CHAR_TOKENS.indexOf(char) === -1; }; var isSpecialChar = function isSpecialChar(char) { return SPECIAL_CHARS.indexOf(char) >= 0; }; var isEscapableChar = function isEscapableChar(char) { return char === openTag || char === closeTag || char === char_2; }; var isEscapeChar = function isEscapeChar(char) { return char === char_2; }; var onSkip = function onSkip() { col++; }; var unq = function unq(val) { return unquote(trimChar(val, char_9)); }; var chars = createCharGrabber(buffer, { onSkip: onSkip }); /** * Emits newly created token to subscriber * @param {Number} type * @param {String} value */ function emitToken(type, value) { var token = createToken(type, value, row, col); onToken(token); tokenIndex += 1; tokens[tokenIndex] = token; } function nextTagState(tagChars, isSingleValueTag) { if (tagMode === TAG_STATE_ATTR) { var validAttrName = function validAttrName(char) { return !(char === char_10 || isWhiteSpace(char)); }; var _name = tagChars.grabWhile(validAttrName); var isEnd = tagChars.isLast(); var isValue = tagChars.getCurr() !== char_10; tagChars.skip(); if (isEnd || isValue) { emitToken(TYPE_ATTR_VALUE, unq(_name)); } else { emitToken(TYPE_ATTR_NAME, _name); } if (isEnd) { return TAG_STATE_NAME; } if (isValue) { return TAG_STATE_ATTR; } return TAG_STATE_VALUE; } if (tagMode === TAG_STATE_VALUE) { var stateSpecial = false; var validAttrValue = function validAttrValue(char) { // const isEQ = char === EQ; var isQM = char === char_9; var prevChar = tagChars.getPrev(); var nextChar = tagChars.getNext(); var isPrevSLASH = prevChar === char_2; var isNextEQ = nextChar === char_10; var isWS = isWhiteSpace(char); // const isPrevWS = isWhiteSpace(prevChar); var isNextWS = isWhiteSpace(nextChar); if (stateSpecial && isSpecialChar(char)) { return true; } if (isQM && !isPrevSLASH) { stateSpecial = !stateSpecial; if (!stateSpecial && !(isNextEQ || isNextWS)) { return false; } } if (!isSingleValueTag) { return isWS === false; // return (isEQ || isWS) === false; } return true; }; var _name2 = tagChars.grabWhile(validAttrValue); tagChars.skip(); emitToken(TYPE_ATTR_VALUE, unq(_name2)); if (tagChars.isLast()) { return TAG_STATE_NAME; } return TAG_STATE_ATTR; } var validName = function validName(char) { return !(char === char_10 || isWhiteSpace(char) || tagChars.isLast()); }; var name = tagChars.grabWhile(validName); emitToken(TYPE_TAG, name); tagChars.skip(); // in cases when we has [url=someval]GET[/url] and we dont need to parse all if (isSingleValueTag) { return TAG_STATE_VALUE; } var hasEQ = tagChars.includes(char_10); return hasEQ ? TAG_STATE_ATTR : TAG_STATE_VALUE; } function stateTag() { var currChar = chars.getCurr(); if (currChar === openTag) { var nextChar = chars.getNext(); chars.skip(); // detect case where we have '[My word [tag][/tag]' or we have '[My last line word' var substr = chars.substrUntilChar(closeTag); var hasInvalidChars = substr.length === 0 || substr.indexOf(openTag) >= 0; if (isCharReserved(nextChar) || hasInvalidChars || chars.isLast()) { emitToken(TYPE_WORD, currChar); return STATE_WORD; } // [myTag ] var isNoAttrsInTag = substr.indexOf(char_10) === -1; // [/myTag] var isClosingTag = substr[0] === char_5; if (isNoAttrsInTag || isClosingTag) { var name = chars.grabWhile(function (char) { return char !== closeTag; }); chars.skip(); // skip closeTag emitToken(TYPE_TAG, name); return STATE_WORD; } return STATE_TAG_ATTRS; } return STATE_WORD; } function stateAttrs() { var silent = true; var tagStr = chars.grabWhile(function (char) { return char !== closeTag; }, silent); var tagGrabber = createCharGrabber(tagStr, { onSkip: onSkip }); var hasSpace = tagGrabber.includes(char_8); tagMode = TAG_STATE_NAME; while (tagGrabber.hasNext()) { tagMode = nextTagState(tagGrabber, !hasSpace); } chars.skip(); // skip closeTag return STATE_WORD; } function stateWord() { if (isNewLine(chars.getCurr())) { emitToken(TYPE_NEW_LINE, chars.getCurr()); chars.skip(); col = 0; row++; return STATE_WORD; } if (isWhiteSpace(chars.getCurr())) { emitToken(TYPE_SPACE, chars.grabWhile(isWhiteSpace)); return STATE_WORD; } if (chars.getCurr() === openTag) { if (chars.includes(closeTag)) { return STATE_TAG; } emitToken(TYPE_WORD, chars.getCurr()); chars.skip(); return STATE_WORD; } if (escapeTags) { if (isEscapeChar(chars.getCurr())) { var currChar = chars.getCurr(); var nextChar = chars.getNext(); chars.skip(); // skip the \ without emitting anything if (isEscapableChar(nextChar)) { chars.skip(); // skip past the [, ] or \ as well emitToken(TYPE_WORD, nextChar); return STATE_WORD; } emitToken(TYPE_WORD, currChar); return STATE_WORD; } var isChar = function isChar(char) { return isCharToken(char) && !isEscapeChar(char); }; emitToken(TYPE_WORD, chars.grabWhile(isChar)); return STATE_WORD; } emitToken(TYPE_WORD, chars.grabWhile(isCharToken)); return STATE_WORD; } function tokenize() { stateMode = STATE_WORD; while (chars.hasNext()) { switch (stateMode) { case STATE_TAG: stateMode = stateTag(); break; case STATE_TAG_ATTRS: stateMode = stateAttrs(); break; case STATE_WORD: stateMode = stateWord(); break; default: stateMode = STATE_WORD; break; } } tokens.length = tokenIndex + 1; return tokens; } function isTokenNested(token) { var value = openTag + char_5 + token.getValue(); // potential bottleneck return buffer.indexOf(value) > -1; } return { tokenize: tokenize, isTokenNested: isTokenNested }; } /** * @public * @param {String} input * @param {Object} opts * @param {Function} opts.createTokenizer * @param {Array<string>} opts.onlyAllowTags * @param {String} opts.openTag * @param {String} opts.closeTag * @param {Boolean} opts.enableEscapeTags * @return {Array} */ var parse = function parse(input, opts) { if (opts === void 0) { opts = {}; } var options = opts; var openTag = options.openTag || char_7; var closeTag = options.closeTag || char_6; var tokenizer = null; /** * Result AST of nodes * @private * @type {NodeList} */ var nodes = createList(); /** * Temp buffer of nodes that's nested to another node * @private * @type {NodeList} */ var nestedNodes = createList(); /** * Temp buffer of nodes [tag..]...[/tag] * @private * @type {NodeList} */ var tagNodes = createList(); /** * Temp buffer of tag attributes * @private * @type {NodeList} */ var tagNodesAttrName = createList(); /** * Cache for nested tags checks */ var nestedTagsMap = new Set(); /** * * @param token * @returns {boolean} */ var isTokenNested = function isTokenNested(token) { var value = token.getValue(); if (!nestedTagsMap.has(value) && tokenizer.isTokenNested && tokenizer.isTokenNested(token)) { nestedTagsMap.add(value); return true; } return nestedTagsMap.has(value); }; /** * @param tagName * @returns {boolean} */ var isTagNested = function isTagNested(tagName) { return Boolean(nestedTagsMap.has(tagName)); }; /** * @private * @param {String} value * @return {boolean} */ var isAllowedTag = function isAllowedTag(value) { if (options.onlyAllowTags && options.onlyAllowTags.length) { return options.onlyAllowTags.indexOf(value) >= 0; } return true; }; /** * Flushes temp tag nodes and its attributes buffers * @private * @return {Array} */ var flushTagNodes = function flushTagNodes() { if (tagNodes.flushLast()) { tagNodesAttrName.flushLast(); } }; /** * @private * @return {Array} */ var getNodes = function getNodes() { var lastNestedNode = nestedNodes.getLast(); if (lastNestedNode && Array.isArray(lastNestedNode.content)) { return lastNestedNode.content; } return nodes.toArray(); }; /** * @private * @param {string|TagNode} node */ var appendNodes = function appendNodes(node) { var items = getNodes(); if (Array.isArray(items)) { if (lib_3(node)) { if (isAllowedTag(node.tag)) { items.push(node.toTagNode()); } else { items.push(node.toTagStart({ openTag: openTag, closeTag: closeTag })); if (node.content.length) { node.content.forEach(function (item) { items.push(item); }); items.push(node.toTagEnd({ openTag: openTag, closeTag: closeTag })); } } } else { items.push(node); } } }; /** * @private * @param {Token} token */ var handleTagStart = function handleTagStart(token) { flushTagNodes(); var tagNode = TagNode.create(token.getValue()); var isNested = isTokenNested(token); tagNodes.push(tagNode); if (isNested) { nestedNodes.push(tagNode); } else { appendNodes(tagNode); } }; /** * @private * @param {Token} token */ var handleTagEnd = function handleTagEnd(token) { flushTagNodes(); var lastNestedNode = nestedNodes.flushLast(); if (lastNestedNode) { appendNodes(lastNestedNode); } else if (typeof options.onError === 'function') { var tag = token.getValue(); var line = token.getLine(); var column = token.getColumn(); options.onError({ message: "Inconsistent tag '" + tag + "' on line " + line + " and column " + column, tagName: tag, lineNumber: line, columnNumber: column }); } }; /** * @private * @param {Token} token */ var handleTag = function handleTag(token) { // [tag] if (token.isStart()) { handleTagStart(token); } // [/tag] if (token.isEnd()) { handleTagEnd(token); } }; /** * @private * @param {Token} token */ var handleNode = function handleNode(token) { /** * @type {TagNode} */ var lastTagNode = tagNodes.getLast(); var tokenValue = token.getValue(); var isNested = isTagNested(token); if (lastTagNode) { if (token.isAttrName()) { tagNodesAttrName.push(tokenValue); lastTagNode.attr(tagNodesAttrName.getLast(), ''); } else if (token.isAttrValue()) { var attrName = tagNodesAttrName.getLast(); if (attrName) { lastTagNode.attr(attrName, tokenValue); tagNodesAttrName.flushLast(); } else { lastTagNode.attr(tokenValue, tokenValue); } } else if (token.isText()) { if (isNested) { lastTagNode.append(tokenValue); } else { appendNodes(tokenValue); } } else if (token.isTag()) { // if tag is not allowed, just past it as is appendNodes(token.toString()); } } else if (token.isText()) { appendNodes(tokenValue); } else if (token.isTag()) { // if tag is not allowed, just past it as is appendNodes(token.toString()); } }; /** * @private * @param {Token} token */ var onToken = function onToken(token) { if (token.isTag()) { handleTag(token); } else { handleNode(token); } }; tokenizer = (opts.createTokenizer ? opts.createTokenizer : createLexer)(input, { onToken: onToken, onlyAllowTags: options.onlyAllowTags, openTag: openTag, closeTag: closeTag, enableEscapeTags: options.enableEscapeTags }); // eslint-disable-next-line no-unused-vars var tokens = tokenizer.tokenize(); return nodes.toArray(); }; exports.TagNode = TagNode_2; exports.default = parse; exports.parse = parse; Object.defineProperty(exports, '__esModule', { value: true }); })));