diff --git a/static/js/paged.polyfill.js b/static/js/paged.polyfill.js index bea1e19..1b7048a 100644 --- a/static/js/paged.polyfill.js +++ b/static/js/paged.polyfill.js @@ -373,25 +373,25 @@ return Promise.all(promises); } - /** + /** * Triggers a hook to run all functions synchronously * @example this.content.trigger(args).then(function(){...}); * @return {Array} results */ - triggerSync(){ - var args = arguments; - var context = this.context; - var results = []; + triggerSync(){ + var args = arguments; + var context = this.context; + var results = []; - this.hooks.forEach(function(task) { - var executing = task.apply(context, args); + this.hooks.forEach(function(task) { + var executing = task.apply(context, args); - results.push(executing); - }); + results.push(executing); + }); - return results; - } + return results; + } // Adds a function to be run before a hook completes list(){ @@ -440,7 +440,7 @@ */ function UUID() { var d = new Date().getTime(); - if (typeof performance !== "undefined" && typeof performance.now === "function"){ + if (typeof performance !== "undefined" && typeof performance.now === "function") { d += performance.now(); //use high-precision timer if available } return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { @@ -452,7 +452,7 @@ function attr(element, attributes) { for (var i = 0; i < attributes.length; i++) { - if(element.hasAttribute(attributes[i])) { + if (element.hasAttribute(attributes[i])) { return element.getAttribute(attributes[i]); } } @@ -466,6 +466,7 @@ throw new TypeError("`CSS.escape` requires an argument."); } var string = String(value); + var length = string.length; var index = -1; var codeUnit; @@ -473,6 +474,9 @@ var firstCodeUnit = string.charCodeAt(0); while (++index < length) { codeUnit = string.charCodeAt(index); + + + // Note: there’s no need to special-case astral symbols, surrogate // pairs, or lone surrogates. @@ -514,6 +518,15 @@ continue; } + // support for period character in id + if (codeUnit == 0x002E) { + if (string.charAt(0) == "#") { + result += "\\."; + continue; + } + } + + // If the character is not handled by one of the above rules and is // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to @@ -1274,11 +1287,28 @@ if (!renderedNode) { // Find closest element with data-ref renderedNode = findElement(prevValidNode(temp), rendered); - return; + // Check if temp is the last rendered node at its level. + if (!temp.nextSibling) { + // We need to ensure that the previous sibling of temp is fully rendered. + const renderedNodeFromSource = findElement(renderedNode, source); + const walker = document.createTreeWalker(renderedNodeFromSource, NodeFilter.SHOW_ELEMENT); + const lastChildOfRenderedNodeFromSource = walker.lastChild(); + const lastChildOfRenderedNodeMatchingFromRendered = findElement(lastChildOfRenderedNodeFromSource, rendered); + // Check if we found that the last child in source + if (!lastChildOfRenderedNodeMatchingFromRendered) { + // Pending content to be rendered before virtual break token + return; + } + // Otherwise we will return a break token as per below + } + // renderedNode is actually the last unbroken box that does not overflow. + // Break Token is therefore the next sibling of renderedNode within source node. + node = findElement(renderedNode, source).nextSibling; + offset = 0; + } else { + node = findElement(renderedNode, source); + offset = 0; } - - node = findElement(renderedNode, source); - offset = 0; } else { renderedNode = findElement(container, rendered); @@ -1334,7 +1364,8 @@ if (overflow) { breakToken = this.createBreakToken(overflow, rendered, source); - if (breakToken["node"] && breakToken["offset"] && breakToken["node"].textContent) { + // breakToken is nullable + if (breakToken && breakToken["node"] && breakToken["offset"] && breakToken["node"].textContent) { breakLetter = breakToken["node"].textContent.charAt(breakToken["offset"]); } else { breakLetter = undefined; @@ -1385,7 +1416,7 @@ if (node) { let pos = getBoundingClientRect(node); - let left = Math.floor(pos.left); + let left = Math.round(pos.left); let right = Math.floor(pos.right); if (!range && left >= end) { @@ -1448,7 +1479,7 @@ } // Skip children - if (skip || right < end) { + if (skip || right <= end) { next = nodeAfter(node, rendered); if (next) { walker = walk(next, rendered); @@ -1671,7 +1702,7 @@ // page.dataset.pageNumber = index; page.dataset.pageNumber = index; - page.setAttribute('id', id); + page.setAttribute("id", id); if (this.name) { page.classList.add("pagedjs_" + this.name + "_page"); @@ -2695,18 +2726,19 @@ eventEmitter(Chunker.prototype); // + // list + // ┌──────┐ + // ┌──────────────┼─head │ + // │ │ tail─┼──────────────┐ + // │ └──────┘ │ + // ▼ ▼ // item item item item - // /------\ /------\ /------\ /------\ - // | data | | data | | data | | data | - // null <--+-prev |<---+-prev |<---+-prev |<---+-prev | - // | next-+--->| next-+--->| next-+--->| next-+--> null - // \------/ \------/ \------/ \------/ - // ^ ^ - // | list | - // | /------\ | - // \--------------+-head | | - // | tail-+--------------/ - // \------/ + // ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ + // null ◀──┼─prev │◀───┼─prev │◀───┼─prev │◀───┼─prev │ + // │ next─┼───▶│ next─┼───▶│ next─┼───▶│ next─┼──▶ null + // ├──────┤ ├──────┤ ├──────┤ ├──────┤ + // │ data │ │ data │ │ data │ │ data │ + // └──────┘ └──────┘ └──────┘ └──────┘ // function createItem(data) { @@ -3220,7 +3252,7 @@ this.remove(oldItem); }; - var list = List; + var List_1 = List; var createCustomError = function createCustomError(name, message) { // use Object.create(), because some VMs prevent setting line/column otherwise @@ -3290,8 +3322,8 @@ ].filter(Boolean).join('\n'); } - var CssSyntaxError = function(message, source, offset, line, column) { - var error = createCustomError('CssSyntaxError', message); + var SyntaxError$1 = function(message, source, offset, line, column) { + var error = createCustomError('SyntaxError', message); error.source = source; error.offset = offset; @@ -3320,74 +3352,37 @@ return error; }; - var error = CssSyntaxError; - - // token types (note: value shouldn't intersect with used char codes) - var WHITESPACE = 1; - var IDENTIFIER = 2; - var NUMBER = 3; - var STRING = 4; - var COMMENT = 5; - var PUNCTUATOR = 6; - var CDO = 7; - var CDC = 8; - var ATKEYWORD = 14; - var FUNCTION = 15; - var URL$1 = 16; - var RAW = 17; - - var TAB = 9; - var N = 10; - var F = 12; - var R = 13; - var SPACE = 32; + var _SyntaxError = SyntaxError$1; + // CSS Syntax Module Level 3 + // https://www.w3.org/TR/css-syntax-3/ var TYPE = { - WhiteSpace: WHITESPACE, - Identifier: IDENTIFIER, - Number: NUMBER, - String: STRING, - Comment: COMMENT, - Punctuator: PUNCTUATOR, - CDO: CDO, - CDC: CDC, - AtKeyword: ATKEYWORD, - Function: FUNCTION, - Url: URL$1, - Raw: RAW, - - ExclamationMark: 33, // ! - QuotationMark: 34, // " - NumberSign: 35, // # - DollarSign: 36, // $ - PercentSign: 37, // % - Ampersand: 38, // & - Apostrophe: 39, // ' - LeftParenthesis: 40, // ( - RightParenthesis: 41, // ) - Asterisk: 42, // * - PlusSign: 43, // + - Comma: 44, // , - HyphenMinus: 45, // - - FullStop: 46, // . - Solidus: 47, // / - Colon: 58, // : - Semicolon: 59, // ; - LessThanSign: 60, // < - EqualsSign: 61, // = - GreaterThanSign: 62, // > - QuestionMark: 63, // ? - CommercialAt: 64, // @ - LeftSquareBracket: 91, // [ - Backslash: 92, // \ - RightSquareBracket: 93, // ] - CircumflexAccent: 94, // ^ - LowLine: 95, // _ - GraveAccent: 96, // ` - LeftCurlyBracket: 123, // { - VerticalLine: 124, // | - RightCurlyBracket: 125, // } - Tilde: 126 // ~ + EOF: 0, // + Ident: 1, // + Function: 2, // + AtKeyword: 3, // + Hash: 4, // + String: 5, // + BadString: 6, // + Url: 7, // + BadUrl: 8, // + Delim: 9, // + Number: 10, // + Percentage: 11, // + Dimension: 12, // + WhiteSpace: 13, // + CDO: 14, // + CDC: 15, // + Colon: 16, // : + Semicolon: 17, // ; + Comma: 18, // , + LeftSquareBracket: 19, // <[-token> + RightSquareBracket: 20, // <]-token> + LeftParenthesis: 21, // <(-token> + RightParenthesis: 22, // <)-token> + LeftCurlyBracket: 23, // <{-token> + RightCurlyBracket: 24, // <}-token> + Comment: 25 }; var NAME = Object.keys(TYPE).reduce(function(result, key) { @@ -3395,6 +3390,199 @@ return result; }, {}); + var _const = { + TYPE: TYPE, + NAME: NAME + }; + + var EOF = 0; + + // https://drafts.csswg.org/css-syntax-3/ + // § 4.2. Definitions + + // digit + // A code point between U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9). + function isDigit(code) { + return code >= 0x0030 && code <= 0x0039; + } + + // hex digit + // A digit, or a code point between U+0041 LATIN CAPITAL LETTER A (A) and U+0046 LATIN CAPITAL LETTER F (F), + // or a code point between U+0061 LATIN SMALL LETTER A (a) and U+0066 LATIN SMALL LETTER F (f). + function isHexDigit(code) { + return ( + isDigit(code) || // 0 .. 9 + (code >= 0x0041 && code <= 0x0046) || // A .. F + (code >= 0x0061 && code <= 0x0066) // a .. f + ); + } + + // uppercase letter + // A code point between U+0041 LATIN CAPITAL LETTER A (A) and U+005A LATIN CAPITAL LETTER Z (Z). + function isUppercaseLetter(code) { + return code >= 0x0041 && code <= 0x005A; + } + + // lowercase letter + // A code point between U+0061 LATIN SMALL LETTER A (a) and U+007A LATIN SMALL LETTER Z (z). + function isLowercaseLetter(code) { + return code >= 0x0061 && code <= 0x007A; + } + + // letter + // An uppercase letter or a lowercase letter. + function isLetter(code) { + return isUppercaseLetter(code) || isLowercaseLetter(code); + } + + // non-ASCII code point + // A code point with a value equal to or greater than U+0080 . + function isNonAscii(code) { + return code >= 0x0080; + } + + // name-start code point + // A letter, a non-ASCII code point, or U+005F LOW LINE (_). + function isNameStart(code) { + return isLetter(code) || isNonAscii(code) || code === 0x005F; + } + + // name code point + // A name-start code point, a digit, or U+002D HYPHEN-MINUS (-). + function isName(code) { + return isNameStart(code) || isDigit(code) || code === 0x002D; + } + + // non-printable code point + // A code point between U+0000 NULL and U+0008 BACKSPACE, or U+000B LINE TABULATION, + // or a code point between U+000E SHIFT OUT and U+001F INFORMATION SEPARATOR ONE, or U+007F DELETE. + function isNonPrintable(code) { + return ( + (code >= 0x0000 && code <= 0x0008) || + (code === 0x000B) || + (code >= 0x000E && code <= 0x001F) || + (code === 0x007F) + ); + } + + // newline + // U+000A LINE FEED. Note that U+000D CARRIAGE RETURN and U+000C FORM FEED are not included in this definition, + // as they are converted to U+000A LINE FEED during preprocessing. + // TODO: we doesn't do a preprocessing, so check a code point for U+000D CARRIAGE RETURN and U+000C FORM FEED + function isNewline(code) { + return code === 0x000A || code === 0x000D || code === 0x000C; + } + + // whitespace + // A newline, U+0009 CHARACTER TABULATION, or U+0020 SPACE. + function isWhiteSpace(code) { + return isNewline(code) || code === 0x0020 || code === 0x0009; + } + + // § 4.3.8. Check if two code points are a valid escape + function isValidEscape(first, second) { + // If the first code point is not U+005C REVERSE SOLIDUS (\), return false. + if (first !== 0x005C) { + return false; + } + + // Otherwise, if the second code point is a newline or EOF, return false. + if (isNewline(second) || second === EOF) { + return false; + } + + // Otherwise, return true. + return true; + } + + // § 4.3.9. Check if three code points would start an identifier + function isIdentifierStart(first, second, third) { + // Look at the first code point: + + // U+002D HYPHEN-MINUS + if (first === 0x002D) { + // If the second code point is a name-start code point or a U+002D HYPHEN-MINUS, + // or the second and third code points are a valid escape, return true. Otherwise, return false. + return ( + isNameStart(second) || + second === 0x002D || + isValidEscape(second, third) + ); + } + + // name-start code point + if (isNameStart(first)) { + // Return true. + return true; + } + + // U+005C REVERSE SOLIDUS (\) + if (first === 0x005C) { + // If the first and second code points are a valid escape, return true. Otherwise, return false. + return isValidEscape(first, second); + } + + // anything else + // Return false. + return false; + } + + // § 4.3.10. Check if three code points would start a number + function isNumberStart(first, second, third) { + // Look at the first code point: + + // U+002B PLUS SIGN (+) + // U+002D HYPHEN-MINUS (-) + if (first === 0x002B || first === 0x002D) { + // If the second code point is a digit, return true. + if (isDigit(second)) { + return 2; + } + + // Otherwise, if the second code point is a U+002E FULL STOP (.) + // and the third code point is a digit, return true. + // Otherwise, return false. + return second === 0x002E && isDigit(third) ? 3 : 0; + } + + // U+002E FULL STOP (.) + if (first === 0x002E) { + // If the second code point is a digit, return true. Otherwise, return false. + return isDigit(second) ? 2 : 0; + } + + // digit + if (isDigit(first)) { + // Return true. + return 1; + } + + // anything else + // Return false. + return 0; + } + + // + // Misc + // + + // detect BOM (https://en.wikipedia.org/wiki/Byte_order_mark) + function isBOM(code) { + // UTF-16BE + if (code === 0xFEFF) { + return 1; + } + + // UTF-16LE + if (code === 0xFFFE) { + return 1; + } + + return 0; + } + + // Fast code category + // // https://drafts.csswg.org/css-syntax/#tokenizer-definitions // > non-ASCII code point // > A code point with a value equal to or greater than U+0080 @@ -3402,156 +3590,84 @@ // > A letter, a non-ASCII code point, or U+005F LOW LINE (_). // > name code point // > A name-start code point, a digit, or U+002D HYPHEN-MINUS (-) - // That means only ASCII code points has a special meaning and we a maps for 0..127 codes only - var SafeUint32Array = typeof Uint32Array !== 'undefined' ? Uint32Array : Array; // fallback on Array when TypedArray is not supported - var SYMBOL_TYPE = new SafeUint32Array(0x80); - var PUNCTUATION = new SafeUint32Array(0x80); - var STOP_URL_RAW = new SafeUint32Array(0x80); + // That means only ASCII code points has a special meaning and we define a maps for 0..127 codes only + var CATEGORY = new Array(0x80); + charCodeCategory.Eof = 0x80; + charCodeCategory.WhiteSpace = 0x82; + charCodeCategory.Digit = 0x83; + charCodeCategory.NameStart = 0x84; + charCodeCategory.NonPrintable = 0x85; - for (var i = 0; i < SYMBOL_TYPE.length; i++) { - SYMBOL_TYPE[i] = IDENTIFIER; + for (var i = 0; i < CATEGORY.length; i++) { + switch (true) { + case isWhiteSpace(i): + CATEGORY[i] = charCodeCategory.WhiteSpace; + break; + + case isDigit(i): + CATEGORY[i] = charCodeCategory.Digit; + break; + + case isNameStart(i): + CATEGORY[i] = charCodeCategory.NameStart; + break; + + case isNonPrintable(i): + CATEGORY[i] = charCodeCategory.NonPrintable; + break; + + default: + CATEGORY[i] = i || charCodeCategory.Eof; + } } - // fill categories - [ - TYPE.ExclamationMark, // ! - TYPE.QuotationMark, // " - TYPE.NumberSign, // # - TYPE.DollarSign, // $ - TYPE.PercentSign, // % - TYPE.Ampersand, // & - TYPE.Apostrophe, // ' - TYPE.LeftParenthesis, // ( - TYPE.RightParenthesis, // ) - TYPE.Asterisk, // * - TYPE.PlusSign, // + - TYPE.Comma, // , - TYPE.HyphenMinus, // - - TYPE.FullStop, // . - TYPE.Solidus, // / - TYPE.Colon, // : - TYPE.Semicolon, // ; - TYPE.LessThanSign, // < - TYPE.EqualsSign, // = - TYPE.GreaterThanSign, // > - TYPE.QuestionMark, // ? - TYPE.CommercialAt, // @ - TYPE.LeftSquareBracket, // [ - // TYPE.Backslash, // \ - TYPE.RightSquareBracket, // ] - TYPE.CircumflexAccent, // ^ - // TYPE.LowLine, // _ - TYPE.GraveAccent, // ` - TYPE.LeftCurlyBracket, // { - TYPE.VerticalLine, // | - TYPE.RightCurlyBracket, // } - TYPE.Tilde // ~ - ].forEach(function(key) { - SYMBOL_TYPE[Number(key)] = PUNCTUATOR; - PUNCTUATION[Number(key)] = PUNCTUATOR; - }); - - for (var i = 48; i <= 57; i++) { - SYMBOL_TYPE[i] = NUMBER; + function charCodeCategory(code) { + return code < 0x80 ? CATEGORY[code] : charCodeCategory.NameStart; } + var charCodeDefinitions = { + isDigit: isDigit, + isHexDigit: isHexDigit, + isUppercaseLetter: isUppercaseLetter, + isLowercaseLetter: isLowercaseLetter, + isLetter: isLetter, + isNonAscii: isNonAscii, + isNameStart: isNameStart, + isName: isName, + isNonPrintable: isNonPrintable, + isNewline: isNewline, + isWhiteSpace: isWhiteSpace, + isValidEscape: isValidEscape, + isIdentifierStart: isIdentifierStart, + isNumberStart: isNumberStart, - SYMBOL_TYPE[SPACE] = WHITESPACE; - SYMBOL_TYPE[TAB] = WHITESPACE; - SYMBOL_TYPE[N] = WHITESPACE; - SYMBOL_TYPE[R] = WHITESPACE; - SYMBOL_TYPE[F] = WHITESPACE; - - SYMBOL_TYPE[TYPE.Apostrophe] = STRING; - SYMBOL_TYPE[TYPE.QuotationMark] = STRING; - - STOP_URL_RAW[SPACE] = 1; - STOP_URL_RAW[TAB] = 1; - STOP_URL_RAW[N] = 1; - STOP_URL_RAW[R] = 1; - STOP_URL_RAW[F] = 1; - STOP_URL_RAW[TYPE.Apostrophe] = 1; - STOP_URL_RAW[TYPE.QuotationMark] = 1; - STOP_URL_RAW[TYPE.LeftParenthesis] = 1; - STOP_URL_RAW[TYPE.RightParenthesis] = 1; - - // whitespace is punctuation ... - PUNCTUATION[SPACE] = PUNCTUATOR; - PUNCTUATION[TAB] = PUNCTUATOR; - PUNCTUATION[N] = PUNCTUATOR; - PUNCTUATION[R] = PUNCTUATOR; - PUNCTUATION[F] = PUNCTUATOR; - // ... hyper minus is not - PUNCTUATION[TYPE.HyphenMinus] = 0; - - var _const = { - TYPE: TYPE, - NAME: NAME, - - SYMBOL_TYPE: SYMBOL_TYPE, - PUNCTUATION: PUNCTUATION, - STOP_URL_RAW: STOP_URL_RAW + isBOM: isBOM, + charCodeCategory: charCodeCategory }; - var PUNCTUATION$1 = _const.PUNCTUATION; - var STOP_URL_RAW$1 = _const.STOP_URL_RAW; - var TYPE$1 = _const.TYPE; - var FULLSTOP = TYPE$1.FullStop; - var PLUSSIGN = TYPE$1.PlusSign; - var HYPHENMINUS = TYPE$1.HyphenMinus; - var PUNCTUATOR$1 = TYPE$1.Punctuator; - var TAB$1 = 9; - var N$1 = 10; - var F$1 = 12; - var R$1 = 13; - var SPACE$1 = 32; - var BACK_SLASH = 92; - var E = 101; // 'e'.charCodeAt(0) + var isDigit$1 = charCodeDefinitions.isDigit; + var isHexDigit$1 = charCodeDefinitions.isHexDigit; + var isUppercaseLetter$1 = charCodeDefinitions.isUppercaseLetter; + var isName$1 = charCodeDefinitions.isName; + var isWhiteSpace$1 = charCodeDefinitions.isWhiteSpace; + var isValidEscape$1 = charCodeDefinitions.isValidEscape; - function firstCharOffset(source) { - // detect BOM (https://en.wikipedia.org/wiki/Byte_order_mark) - if (source.charCodeAt(0) === 0xFEFF || // UTF-16BE - source.charCodeAt(0) === 0xFFFE) { // UTF-16LE - return 1; - } - - return 0; - } - - function isHex(code) { - return (code >= 48 && code <= 57) || // 0 .. 9 - (code >= 65 && code <= 70) || // A .. F - (code >= 97 && code <= 102); // a .. f - } - - function isNumber(code) { - return code >= 48 && code <= 57; - } - - function isWhiteSpace(code) { - return code === SPACE$1 || code === TAB$1 || isNewline(code); - } - - function isNewline(code) { - return code === R$1 || code === N$1 || code === F$1; + function getCharCode(source, offset) { + return offset < source.length ? source.charCodeAt(offset) : 0; } function getNewlineLength(source, offset, code) { - if (isNewline(code)) { - if (code === R$1 && offset + 1 < source.length && source.charCodeAt(offset + 1) === N$1) { - return 2; - } - - return 1; + if (code === 13 /* \r */ && getCharCode(source, offset + 1) === 10 /* \n */) { + return 2; } - return 0; + return 1; } function cmpChar(testStr, offset, referenceCode) { var code = testStr.charCodeAt(offset); // code.toLowerCase() for A..Z - if (code >= 65 && code <= 90) { + if (isUppercaseLetter$1(code)) { code = code | 32; } @@ -3569,14 +3685,14 @@ for (var i = start; i < end; i++) { var testCode = testStr.charCodeAt(i); - var refCode = referenceStr.charCodeAt(i - start); + var referenceCode = referenceStr.charCodeAt(i - start); // testCode.toLowerCase() for A..Z - if (testCode >= 65 && testCode <= 90) { + if (isUppercaseLetter$1(testCode)) { testCode = testCode | 32; } - if (testCode !== refCode) { + if (testCode !== referenceCode) { return false; } } @@ -3585,40 +3701,18 @@ } function findWhiteSpaceStart(source, offset) { - while (offset >= 0 && isWhiteSpace(source.charCodeAt(offset))) { - offset--; + for (; offset >= 0; offset--) { + if (!isWhiteSpace$1(source.charCodeAt(offset))) { + break; + } } return offset + 1; } function findWhiteSpaceEnd(source, offset) { - while (offset < source.length && isWhiteSpace(source.charCodeAt(offset))) { - offset++; - } - - return offset; - } - - function findCommentEnd(source, offset) { - var commentEnd = source.indexOf('*/', offset); - - if (commentEnd === -1) { - return source.length; - } - - return commentEnd + 2; - } - - function findStringEnd(source, offset, quote) { for (; offset < source.length; offset++) { - var code = source.charCodeAt(offset); - - // TODO: bad string - if (code === BACK_SLASH) { - offset++; - } else if (code === quote) { - offset++; + if (!isWhiteSpace$1(source.charCodeAt(offset))) { break; } } @@ -3627,474 +3721,221 @@ } function findDecimalNumberEnd(source, offset) { - while (offset < source.length && isNumber(source.charCodeAt(offset))) { - offset++; - } - - return offset; - } - - function findNumberEnd(source, offset, allowFraction) { - var code; - - offset = findDecimalNumberEnd(source, offset); - - // fraction: .\d+ - if (allowFraction && offset + 1 < source.length && source.charCodeAt(offset) === FULLSTOP) { - code = source.charCodeAt(offset + 1); - - if (isNumber(code)) { - offset = findDecimalNumberEnd(source, offset + 1); - } - } - - // exponent: e[+-]\d+ - if (offset + 1 < source.length) { - if ((source.charCodeAt(offset) | 32) === E) { // case insensitive check for `e` - code = source.charCodeAt(offset + 1); - - if (code === PLUSSIGN || code === HYPHENMINUS) { - if (offset + 2 < source.length) { - code = source.charCodeAt(offset + 2); - } - } - - if (isNumber(code)) { - offset = findDecimalNumberEnd(source, offset + 2); - } + for (; offset < source.length; offset++) { + if (!isDigit$1(source.charCodeAt(offset))) { + break; } } return offset; } - // skip escaped unicode sequence that can ends with space - // [0-9a-f]{1,6}(\r\n|[ \n\r\t\f])? - function findEscapeEnd(source, offset) { - for (var i = 0; i < 7 && offset + i < source.length; i++) { - var code = source.charCodeAt(offset + i); + // § 4.3.7. Consume an escaped code point + function consumeEscaped(source, offset) { + // It assumes that the U+005C REVERSE SOLIDUS (\) has already been consumed and + // that the next input code point has already been verified to be part of a valid escape. + offset += 2; - if (i !== 6 && isHex(code)) { + // hex digit + if (isHexDigit$1(getCharCode(source, offset - 1))) { + // Consume as many hex digits as possible, but no more than 5. + // Note that this means 1-6 hex digits have been consumed in total. + for (var maxOffset = Math.min(source.length, offset + 5); offset < maxOffset; offset++) { + if (!isHexDigit$1(getCharCode(source, offset))) { + break; + } + } + + // If the next input code point is whitespace, consume it as well. + var code = getCharCode(source, offset); + if (isWhiteSpace$1(code)) { + offset += getNewlineLength(source, offset, code); + } + } + + return offset; + } + + // §4.3.11. Consume a name + // Note: This algorithm does not do the verification of the first few code points that are necessary + // to ensure the returned code points would constitute an . If that is the intended use, + // ensure that the stream starts with an identifier before calling this algorithm. + function consumeName(source, offset) { + // Let result initially be an empty string. + // Repeatedly consume the next input code point from the stream: + for (; offset < source.length; offset++) { + var code = source.charCodeAt(offset); + + // name code point + if (isName$1(code)) { + // Append the code point to result. continue; } - if (i > 0) { - offset += i - 1 + getNewlineLength(source, offset + i, code); - if (code === SPACE$1 || code === TAB$1) { - offset++; - } + // the stream starts with a valid escape + if (isValidEscape$1(code, getCharCode(source, offset + 1))) { + // Consume an escaped code point. Append the returned code point to result. + offset = consumeEscaped(source, offset) - 1; + continue; } + // anything else + // Reconsume the current input code point. Return result. break; } return offset; } - function findIdentifierEnd(source, offset) { - for (; offset < source.length; offset++) { - var code = source.charCodeAt(offset); + // §4.3.12. Consume a number + function consumeNumber(source, offset) { + var code = source.charCodeAt(offset); - if (code === BACK_SLASH) { - offset = findEscapeEnd(source, offset + 1); - } else if (code < 0x80 && PUNCTUATION$1[code] === PUNCTUATOR$1) { - break; + // 2. If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), + // consume it and append it to repr. + if (code === 0x002B || code === 0x002D) { + code = source.charCodeAt(offset += 1); + } + + // 3. While the next input code point is a digit, consume it and append it to repr. + if (isDigit$1(code)) { + offset = findDecimalNumberEnd(source, offset + 1); + code = source.charCodeAt(offset); + } + + // 4. If the next 2 input code points are U+002E FULL STOP (.) followed by a digit, then: + if (code === 0x002E && isDigit$1(source.charCodeAt(offset + 1))) { + // 4.1 Consume them. + // 4.2 Append them to repr. + code = source.charCodeAt(offset += 2); + + // 4.3 Set type to "number". + // TODO + + // 4.4 While the next input code point is a digit, consume it and append it to repr. + + offset = findDecimalNumberEnd(source, offset); + } + + // 5. If the next 2 or 3 input code points are U+0045 LATIN CAPITAL LETTER E (E) + // or U+0065 LATIN SMALL LETTER E (e), ... , followed by a digit, then: + if (cmpChar(source, offset, 101 /* e */)) { + var sign = 0; + code = source.charCodeAt(offset + 1); + + // ... optionally followed by U+002D HYPHEN-MINUS (-) or U+002B PLUS SIGN (+) ... + if (code === 0x002D || code === 0x002B) { + sign = 1; + code = source.charCodeAt(offset + 2); + } + + // ... followed by a digit + if (isDigit$1(code)) { + // 5.1 Consume them. + // 5.2 Append them to repr. + + // 5.3 Set type to "number". + // TODO + + // 5.4 While the next input code point is a digit, consume it and append it to repr. + offset = findDecimalNumberEnd(source, offset + 1 + sign + 1); } } return offset; } - function findUrlRawEnd(source, offset) { + // § 4.3.14. Consume the remnants of a bad url + // ... its sole use is to consume enough of the input stream to reach a recovery point + // where normal tokenizing can resume. + function consumeBadUrlRemnants(source, offset) { + // Repeatedly consume the next input code point from the stream: for (; offset < source.length; offset++) { var code = source.charCodeAt(offset); - if (code === BACK_SLASH) { - offset = findEscapeEnd(source, offset + 1); - } else if (code < 0x80 && STOP_URL_RAW$1[code] === 1) { + // U+0029 RIGHT PARENTHESIS ()) + // EOF + if (code === 0x0029) { + // Return. + offset++; break; } + + if (isValidEscape$1(code, getCharCode(source, offset + 1))) { + // Consume an escaped code point. + // Note: This allows an escaped right parenthesis ("\)") to be encountered + // without ending the . This is otherwise identical to + // the "anything else" clause. + offset = consumeEscaped(source, offset); + } } return offset; } var utils = { - firstCharOffset: firstCharOffset, - - isHex: isHex, - isNumber: isNumber, - isWhiteSpace: isWhiteSpace, - isNewline: isNewline, - getNewlineLength: getNewlineLength, + consumeEscaped: consumeEscaped, + consumeName: consumeName, + consumeNumber: consumeNumber, + consumeBadUrlRemnants: consumeBadUrlRemnants, cmpChar: cmpChar, cmpStr: cmpStr, + getNewlineLength: getNewlineLength, findWhiteSpaceStart: findWhiteSpaceStart, - findWhiteSpaceEnd: findWhiteSpaceEnd, - findCommentEnd: findCommentEnd, - findStringEnd: findStringEnd, - findDecimalNumberEnd: findDecimalNumberEnd, - findNumberEnd: findNumberEnd, - findEscapeEnd: findEscapeEnd, - findIdentifierEnd: findIdentifierEnd, - findUrlRawEnd: findUrlRawEnd + findWhiteSpaceEnd: findWhiteSpaceEnd }; - var TYPE$2 = _const.TYPE; + var TYPE$1 = _const.TYPE; var NAME$1 = _const.NAME; - var SYMBOL_TYPE$1 = _const.SYMBOL_TYPE; - var firstCharOffset$1 = utils.firstCharOffset; var cmpStr$1 = utils.cmpStr; - var isNumber$1 = utils.isNumber; - var findWhiteSpaceStart$1 = utils.findWhiteSpaceStart; - var findWhiteSpaceEnd$1 = utils.findWhiteSpaceEnd; - var findCommentEnd$1 = utils.findCommentEnd; - var findStringEnd$1 = utils.findStringEnd; - var findNumberEnd$1 = utils.findNumberEnd; - var findIdentifierEnd$1 = utils.findIdentifierEnd; - var findUrlRawEnd$1 = utils.findUrlRawEnd; - var NULL = 0; - var WHITESPACE$1 = TYPE$2.WhiteSpace; - var IDENTIFIER$1 = TYPE$2.Identifier; - var NUMBER$1 = TYPE$2.Number; - var STRING$1 = TYPE$2.String; - var COMMENT$1 = TYPE$2.Comment; - var PUNCTUATOR$2 = TYPE$2.Punctuator; - var CDO$1 = TYPE$2.CDO; - var CDC$1 = TYPE$2.CDC; - var ATKEYWORD$1 = TYPE$2.AtKeyword; - var FUNCTION$1 = TYPE$2.Function; - var URL$2 = TYPE$2.Url; - var RAW$1 = TYPE$2.Raw; + var EOF$1 = TYPE$1.EOF; + var WHITESPACE = TYPE$1.WhiteSpace; + var COMMENT = TYPE$1.Comment; - var N$2 = 10; - var F$2 = 12; - var R$2 = 13; - var STAR = TYPE$2.Asterisk; - var SLASH = TYPE$2.Solidus; - var FULLSTOP$1 = TYPE$2.FullStop; - var PLUSSIGN$1 = TYPE$2.PlusSign; - var HYPHENMINUS$1 = TYPE$2.HyphenMinus; - var GREATERTHANSIGN = TYPE$2.GreaterThanSign; - var LESSTHANSIGN = TYPE$2.LessThanSign; - var EXCLAMATIONMARK = TYPE$2.ExclamationMark; - var COMMERCIALAT = TYPE$2.CommercialAt; - var QUOTATIONMARK = TYPE$2.QuotationMark; - var APOSTROPHE = TYPE$2.Apostrophe; - var LEFTPARENTHESIS = TYPE$2.LeftParenthesis; - var RIGHTPARENTHESIS = TYPE$2.RightParenthesis; - var LEFTCURLYBRACKET = TYPE$2.LeftCurlyBracket; - var RIGHTCURLYBRACKET = TYPE$2.RightCurlyBracket; - var LEFTSQUAREBRACKET = TYPE$2.LeftSquareBracket; - var RIGHTSQUAREBRACKET = TYPE$2.RightSquareBracket; - - var MIN_BUFFER_SIZE = 16 * 1024; var OFFSET_MASK = 0x00FFFFFF; var TYPE_SHIFT = 24; - var SafeUint32Array$1 = typeof Uint32Array !== 'undefined' ? Uint32Array : Array; // fallback on Array when TypedArray is not supported - function computeLinesAndColumns(tokenizer, source) { - var sourceLength = source.length; - var start = firstCharOffset$1(source); - var lines = tokenizer.lines; - var line = tokenizer.startLine; - var columns = tokenizer.columns; - var column = tokenizer.startColumn; - - if (lines === null || lines.length < sourceLength + 1) { - lines = new SafeUint32Array$1(Math.max(sourceLength + 1024, MIN_BUFFER_SIZE)); - columns = new SafeUint32Array$1(lines.length); - } - - for (var i = start; i < sourceLength; i++) { - var code = source.charCodeAt(i); - - lines[i] = line; - columns[i] = column++; - - if (code === N$2 || code === R$2 || code === F$2) { - if (code === R$2 && i + 1 < sourceLength && source.charCodeAt(i + 1) === N$2) { - i++; - lines[i] = line; - columns[i] = column; - } - - line++; - column = 1; - } - } - - lines[i] = line; - columns[i] = column; - - tokenizer.linesAnsColumnsComputed = true; - tokenizer.lines = lines; - tokenizer.columns = columns; - } - - function tokenLayout(tokenizer, source, startPos) { - var sourceLength = source.length; - var offsetAndType = tokenizer.offsetAndType; - var balance = tokenizer.balance; - var tokenCount = 0; - var prevType = 0; - var offset = startPos; - var anchor = 0; - var balanceCloseCode = 0; - var balanceStart = 0; - var balancePrev = 0; - - if (offsetAndType === null || offsetAndType.length < sourceLength + 1) { - offsetAndType = new SafeUint32Array$1(sourceLength + 1024); - balance = new SafeUint32Array$1(sourceLength + 1024); - } - - while (offset < sourceLength) { - var code = source.charCodeAt(offset); - var type = code < 0x80 ? SYMBOL_TYPE$1[code] : IDENTIFIER$1; - - balance[tokenCount] = sourceLength; - - switch (type) { - case WHITESPACE$1: - offset = findWhiteSpaceEnd$1(source, offset + 1); - break; - - case PUNCTUATOR$2: - switch (code) { - case balanceCloseCode: - balancePrev = balanceStart & OFFSET_MASK; - balanceStart = balance[balancePrev]; - balanceCloseCode = balanceStart >> TYPE_SHIFT; - balance[tokenCount] = balancePrev; - balance[balancePrev++] = tokenCount; - for (; balancePrev < tokenCount; balancePrev++) { - if (balance[balancePrev] === sourceLength) { - balance[balancePrev] = tokenCount; - } - } - break; - - case LEFTSQUAREBRACKET: - balance[tokenCount] = balanceStart; - balanceCloseCode = RIGHTSQUAREBRACKET; - balanceStart = (balanceCloseCode << TYPE_SHIFT) | tokenCount; - break; - - case LEFTCURLYBRACKET: - balance[tokenCount] = balanceStart; - balanceCloseCode = RIGHTCURLYBRACKET; - balanceStart = (balanceCloseCode << TYPE_SHIFT) | tokenCount; - break; - - case LEFTPARENTHESIS: - balance[tokenCount] = balanceStart; - balanceCloseCode = RIGHTPARENTHESIS; - balanceStart = (balanceCloseCode << TYPE_SHIFT) | tokenCount; - break; - } - - // /* - if (code === STAR && prevType === SLASH) { - type = COMMENT$1; - offset = findCommentEnd$1(source, offset + 1); - tokenCount--; // rewrite prev token - break; - } - - // edge case for -.123 and +.123 - if (code === FULLSTOP$1 && (prevType === PLUSSIGN$1 || prevType === HYPHENMINUS$1)) { - if (offset + 1 < sourceLength && isNumber$1(source.charCodeAt(offset + 1))) { - type = NUMBER$1; - offset = findNumberEnd$1(source, offset + 2, false); - tokenCount--; // rewrite prev token - break; - } - } - - // - if (code === HYPHENMINUS$1 && prevType === HYPHENMINUS$1) { - if (offset + 1 < sourceLength && source.charCodeAt(offset + 1) === GREATERTHANSIGN) { - type = CDC$1; - offset = offset + 2; - tokenCount--; // rewrite prev token - break; - } - } - - // ident( - if (code === LEFTPARENTHESIS && prevType === IDENTIFIER$1) { - offset = offset + 1; - tokenCount--; // rewrite prev token - balance[tokenCount] = balance[tokenCount + 1]; - balanceStart--; - - // 4 char length identifier and equal to `url(` (case insensitive) - if (offset - anchor === 4 && cmpStr$1(source, anchor, offset, 'url(')) { - // special case for url() because it can contain any symbols sequence with few exceptions - anchor = findWhiteSpaceEnd$1(source, offset); - code = source.charCodeAt(anchor); - if (code !== LEFTPARENTHESIS && - code !== RIGHTPARENTHESIS && - code !== QUOTATIONMARK && - code !== APOSTROPHE) { - // url( - offsetAndType[tokenCount++] = (URL$2 << TYPE_SHIFT) | offset; - balance[tokenCount] = sourceLength; - - // ws* - if (anchor !== offset) { - offsetAndType[tokenCount++] = (WHITESPACE$1 << TYPE_SHIFT) | anchor; - balance[tokenCount] = sourceLength; - } - - // raw - type = RAW$1; - offset = findUrlRawEnd$1(source, anchor); - } else { - type = URL$2; - } - } else { - type = FUNCTION$1; - } - break; - } - - type = code; - offset = offset + 1; - break; - - case NUMBER$1: - offset = findNumberEnd$1(source, offset + 1, prevType !== FULLSTOP$1); - - // merge number with a preceding dot, dash or plus - if (prevType === FULLSTOP$1 || - prevType === HYPHENMINUS$1 || - prevType === PLUSSIGN$1) { - tokenCount--; // rewrite prev token - } - - break; - - case STRING$1: - offset = findStringEnd$1(source, offset + 1, code); - break; - - default: - anchor = offset; - offset = findIdentifierEnd$1(source, offset); - - // merge identifier with a preceding dash - if (prevType === HYPHENMINUS$1) { - // rewrite prev token - tokenCount--; - // restore prev prev token type - // for case @-prefix-ident - prevType = tokenCount === 0 ? 0 : offsetAndType[tokenCount - 1] >> TYPE_SHIFT; - } - - if (prevType === COMMERCIALAT) { - // rewrite prev token and change type to - tokenCount--; - type = ATKEYWORD$1; - } - } - - offsetAndType[tokenCount++] = (type << TYPE_SHIFT) | offset; - prevType = type; - } - - // finalize arrays - offsetAndType[tokenCount] = offset; - balance[tokenCount] = sourceLength; - balance[sourceLength] = sourceLength; // prevents false positive balance match with any token - while (balanceStart !== 0) { - balancePrev = balanceStart & OFFSET_MASK; - balanceStart = balance[balancePrev]; - balance[balancePrev] = sourceLength; - } - - tokenizer.offsetAndType = offsetAndType; - tokenizer.tokenCount = tokenCount; - tokenizer.balance = balance; - } - - // - // tokenizer - // - - var Tokenizer = function(source, startOffset, startLine, startColumn) { + var TokenStream = function() { this.offsetAndType = null; this.balance = null; - this.lines = null; - this.columns = null; - this.setSource(source, startOffset, startLine, startColumn); + this.reset(); }; - Tokenizer.prototype = { - setSource: function(source, startOffset, startLine, startColumn) { - var safeSource = String(source || ''); - var start = firstCharOffset$1(safeSource); - - this.source = safeSource; - this.firstCharOffset = start; - this.startOffset = typeof startOffset === 'undefined' ? 0 : startOffset; - this.startLine = typeof startLine === 'undefined' ? 1 : startLine; - this.startColumn = typeof startColumn === 'undefined' ? 1 : startColumn; - this.linesAnsColumnsComputed = false; - + TokenStream.prototype = { + reset: function() { this.eof = false; - this.currentToken = -1; + this.tokenIndex = -1; this.tokenType = 0; - this.tokenStart = start; - this.tokenEnd = start; - - tokenLayout(this, safeSource, start); - this.next(); + this.tokenStart = this.firstCharOffset; + this.tokenEnd = this.firstCharOffset; }, lookupType: function(offset) { - offset += this.currentToken; + offset += this.tokenIndex; if (offset < this.tokenCount) { return this.offsetAndType[offset] >> TYPE_SHIFT; } - return NULL; + return EOF$1; }, - lookupNonWSType: function(offset) { - offset += this.currentToken; + lookupOffset: function(offset) { + offset += this.tokenIndex; - for (var type; offset < this.tokenCount; offset++) { - type = this.offsetAndType[offset] >> TYPE_SHIFT; - - if (type !== WHITESPACE$1) { - return type; - } + if (offset < this.tokenCount) { + return this.offsetAndType[offset - 1] & OFFSET_MASK; } - return NULL; + return this.source.length; }, lookupValue: function(offset, referenceStr) { - offset += this.currentToken; + offset += this.tokenIndex; if (offset < this.tokenCount) { return cmpStr$1( @@ -4107,79 +3948,89 @@ return false; }, - getTokenStart: function(tokenNum) { - if (tokenNum === this.currentToken) { + getTokenStart: function(tokenIndex) { + if (tokenIndex === this.tokenIndex) { return this.tokenStart; } - if (tokenNum > 0) { - return tokenNum < this.tokenCount - ? this.offsetAndType[tokenNum - 1] & OFFSET_MASK + if (tokenIndex > 0) { + return tokenIndex < this.tokenCount + ? this.offsetAndType[tokenIndex - 1] & OFFSET_MASK : this.offsetAndType[this.tokenCount] & OFFSET_MASK; } return this.firstCharOffset; }, - getOffsetExcludeWS: function() { - if (this.currentToken > 0) { - if ((this.offsetAndType[this.currentToken - 1] >> TYPE_SHIFT) === WHITESPACE$1) { - return this.currentToken > 1 - ? this.offsetAndType[this.currentToken - 2] & OFFSET_MASK - : this.firstCharOffset; - } - } - return this.tokenStart; - }, - getRawLength: function(startToken, endTokenType1, endTokenType2, includeTokenType2) { + + // TODO: -> skipUntilBalanced + getRawLength: function(startToken, mode) { var cursor = startToken; var balanceEnd; + var offset = this.offsetAndType[Math.max(cursor - 1, 0)] & OFFSET_MASK; + var type; loop: for (; cursor < this.tokenCount; cursor++) { balanceEnd = this.balance[cursor]; - // belance end points to offset before start + // stop scanning on balance edge that points to offset before start token if (balanceEnd < startToken) { break loop; } + type = this.offsetAndType[cursor] >> TYPE_SHIFT; + // check token is stop type - switch (this.offsetAndType[cursor] >> TYPE_SHIFT) { - case endTokenType1: + switch (mode(type, this.source, offset)) { + case 1: break loop; - case endTokenType2: - if (includeTokenType2) { - cursor++; - } + case 2: + cursor++; break loop; default: + offset = this.offsetAndType[cursor] & OFFSET_MASK; + // fast forward to the end of balanced block if (this.balance[balanceEnd] === cursor) { cursor = balanceEnd; } } - } - return cursor - this.currentToken; + return cursor - this.tokenIndex; }, isBalanceEdge: function(pos) { - var balanceStart = this.balance[this.currentToken]; - return balanceStart < pos; + return this.balance[this.tokenIndex] < pos; + }, + isDelim: function(code, offset) { + if (offset) { + return ( + this.lookupType(offset) === TYPE$1.Delim && + this.source.charCodeAt(this.lookupOffset(offset)) === code + ); + } + + return ( + this.tokenType === TYPE$1.Delim && + this.source.charCodeAt(this.tokenStart) === code + ); }, getTokenValue: function() { return this.source.substring(this.tokenStart, this.tokenEnd); }, + getTokenLength: function() { + return this.tokenEnd - this.tokenStart; + }, substrToCursor: function(start) { return this.source.substring(start, this.tokenStart); }, skipWS: function() { - for (var i = this.currentToken, skipTokenCount = 0; i < this.tokenCount; i++, skipTokenCount++) { - if ((this.offsetAndType[i] >> TYPE_SHIFT) !== WHITESPACE$1) { + for (var i = this.tokenIndex, skipTokenCount = 0; i < this.tokenCount; i++, skipTokenCount++) { + if ((this.offsetAndType[i] >> TYPE_SHIFT) !== WHITESPACE) { break; } } @@ -4189,150 +4040,43 @@ } }, skipSC: function() { - while (this.tokenType === WHITESPACE$1 || this.tokenType === COMMENT$1) { + while (this.tokenType === WHITESPACE || this.tokenType === COMMENT) { this.next(); } }, skip: function(tokenCount) { - var next = this.currentToken + tokenCount; + var next = this.tokenIndex + tokenCount; if (next < this.tokenCount) { - this.currentToken = next; + this.tokenIndex = next; this.tokenStart = this.offsetAndType[next - 1] & OFFSET_MASK; next = this.offsetAndType[next]; this.tokenType = next >> TYPE_SHIFT; this.tokenEnd = next & OFFSET_MASK; } else { - this.currentToken = this.tokenCount; + this.tokenIndex = this.tokenCount; this.next(); } }, next: function() { - var next = this.currentToken + 1; + var next = this.tokenIndex + 1; if (next < this.tokenCount) { - this.currentToken = next; + this.tokenIndex = next; this.tokenStart = this.tokenEnd; next = this.offsetAndType[next]; this.tokenType = next >> TYPE_SHIFT; this.tokenEnd = next & OFFSET_MASK; } else { - this.currentToken = this.tokenCount; + this.tokenIndex = this.tokenCount; this.eof = true; - this.tokenType = NULL; + this.tokenType = EOF$1; this.tokenStart = this.tokenEnd = this.source.length; } }, - eat: function(tokenType) { - if (this.tokenType !== tokenType) { - var offset = this.tokenStart; - var message = NAME$1[tokenType] + ' is expected'; - - // tweak message and offset - if (tokenType === IDENTIFIER$1) { - // when identifier is expected but there is a function or url - if (this.tokenType === FUNCTION$1 || this.tokenType === URL$2) { - offset = this.tokenEnd - 1; - message += ' but function found'; - } - } else { - // when test type is part of another token show error for current position + 1 - // e.g. eat(HYPHENMINUS) will fail on "-foo", but pointing on "-" is odd - if (this.source.charCodeAt(this.tokenStart) === tokenType) { - offset = offset + 1; - } - } - - this.error(message, offset); - } - - this.next(); - }, - eatNonWS: function(tokenType) { - this.skipWS(); - this.eat(tokenType); - }, - - consume: function(tokenType) { - var value = this.getTokenValue(); - - this.eat(tokenType); - - return value; - }, - consumeFunctionName: function() { - var name = this.source.substring(this.tokenStart, this.tokenEnd - 1); - - this.eat(FUNCTION$1); - - return name; - }, - consumeNonWS: function(tokenType) { - this.skipWS(); - - return this.consume(tokenType); - }, - - expectIdentifier: function(name) { - if (this.tokenType !== IDENTIFIER$1 || cmpStr$1(this.source, this.tokenStart, this.tokenEnd, name) === false) { - this.error('Identifier `' + name + '` is expected'); - } - - this.next(); - }, - - getLocation: function(offset, filename) { - if (!this.linesAnsColumnsComputed) { - computeLinesAndColumns(this, this.source); - } - - return { - source: filename, - offset: this.startOffset + offset, - line: this.lines[offset], - column: this.columns[offset] - }; - }, - - getLocationRange: function(start, end, filename) { - if (!this.linesAnsColumnsComputed) { - computeLinesAndColumns(this, this.source); - } - - return { - source: filename, - start: { - offset: this.startOffset + start, - line: this.lines[start], - column: this.columns[start] - }, - end: { - offset: this.startOffset + end, - line: this.lines[end], - column: this.columns[end] - } - }; - }, - - error: function(message, offset) { - var location = typeof offset !== 'undefined' && offset < this.source.length - ? this.getLocation(offset) - : this.eof - ? this.getLocation(findWhiteSpaceStart$1(this.source, this.source.length - 1)) - : this.getLocation(this.tokenStart); - - throw new error( - message || 'Unexpected input', - this.source, - location.offset, - location.line, - location.column - ); - }, - dump: function() { - var offset = 0; + var offset = this.firstCharOffset; return Array.prototype.slice.call(this.offsetAndType, 0, this.tokenCount).map(function(item, idx) { var start = offset; @@ -4350,26 +4094,7 @@ } }; - // extend with error class - Tokenizer.CssSyntaxError = error; - - // extend tokenizer with constants - Object.keys(_const).forEach(function(key) { - Tokenizer[key] = _const[key]; - }); - - // extend tokenizer with static methods from utils - Object.keys(utils).forEach(function(key) { - Tokenizer[key] = utils[key]; - }); - - // warm up tokenizer to elimitate code branches that never execute - // fix soft deoptimizations (insufficient type feedback) - new Tokenizer('\n\r\r\n\f//""\'\'/*\r\n\f*/1a;.\\31\t\+2{url(a);func();+1.2e3 -.4e-5 .6e+7}').getLocation(); - - var Tokenizer_1 = Tokenizer; - - var tokenizer = Tokenizer_1; + var TokenStream_1 = TokenStream; function noop$1(value) { return value; @@ -4401,37 +4126,54 @@ ); } - function generateSequence(node, forceBraces, decorate) { + function generateTypeOpts(node) { + switch (node.type) { + case 'Range': + return ( + ' [' + + (node.min === null ? '-∞' : node.min) + + ',' + + (node.max === null ? '∞' : node.max) + + ']' + ); + + default: + throw new Error('Unknown node type `' + node.type + '`'); + } + } + + function generateSequence(node, decorate, forceBraces, compact) { + var combinator = node.combinator === ' ' || compact ? node.combinator : ' ' + node.combinator + ' '; var result = node.terms.map(function(term) { - return generate(term, forceBraces, decorate); - }).join(node.combinator === ' ' ? ' ' : ' ' + node.combinator + ' '); + return generate(term, decorate, forceBraces, compact); + }).join(combinator); if (node.explicit || forceBraces) { - result = (result[0] !== ',' ? '[ ' : '[') + result + ' ]'; + result = (compact || result[0] === ',' ? '[' : '[ ') + result + (compact ? ']' : ' ]'); } return result; } - function generate(node, forceBraces, decorate) { + function generate(node, decorate, forceBraces, compact) { var result; switch (node.type) { case 'Group': result = - generateSequence(node, forceBraces, decorate) + + generateSequence(node, decorate, forceBraces, compact) + (node.disallowEmpty ? '!' : ''); break; case 'Multiplier': // return since node is a composition return ( - generate(node.term, forceBraces, decorate) + + generate(node.term, decorate, forceBraces, compact) + decorate(generateMultiplier(node), node) ); case 'Type': - result = '<' + node.name + '>'; + result = '<' + node.name + (node.opts ? decorate(generateTypeOpts(node.opts), node.opts) : '') + '>'; break; case 'Property': @@ -4469,24 +4211,26 @@ var generate_1 = function(node, options) { var decorate = noop$1; var forceBraces = false; + var compact = false; if (typeof options === 'function') { decorate = options; } else if (options) { forceBraces = Boolean(options.forceBraces); + compact = Boolean(options.compact); if (typeof options.decorate === 'function') { decorate = options.decorate; } } - return generate(node, forceBraces, decorate); + return generate(node, decorate, forceBraces, compact); }; function fromMatchResult(matchResult) { var tokens = matchResult.tokens; var longestMatch = matchResult.longestMatch; var node = longestMatch < tokens.length ? tokens[longestMatch].node : null; - var mismatchOffset = 0; + var mismatchOffset = -1; var entries = 0; var css = ''; @@ -4506,14 +4250,10 @@ css += tokens[i].value; } - if (node === null) { - mismatchOffset = css.length; - } - return { node: node, css: css, - mismatchOffset: mismatchOffset, + mismatchOffset: mismatchOffset === -1 ? css.length : mismatchOffset, last: node === null || entries > 1 }; } @@ -4543,7 +4283,7 @@ return error; }; - var MatchError = function(message, lexer, syntax, node, matchResult) { + var MatchError = function(message, syntax, node, matchResult) { var error = createCustomError('SyntaxMatchError', message); var details = fromMatchResult(matchResult); var mismatchOffset = details.mismatchOffset || 0; @@ -4572,7 +4312,7 @@ return error; }; - var error$1 = { + var error = { SyntaxReferenceError: SyntaxReferenceError, MatchError: MatchError }; @@ -4580,14 +4320,14 @@ var hasOwnProperty = Object.prototype.hasOwnProperty; var keywords = Object.create(null); var properties = Object.create(null); - var HYPHENMINUS$2 = 45; // '-'.charCodeAt() + var HYPHENMINUS = 45; // '-'.charCodeAt() function isCustomProperty(str, offset) { offset = offset || 0; return str.length - offset >= 2 && - str.charCodeAt(offset) === HYPHENMINUS$2 && - str.charCodeAt(offset + 1) === HYPHENMINUS$2; + str.charCodeAt(offset) === HYPHENMINUS && + str.charCodeAt(offset + 1) === HYPHENMINUS; } function getVendorPrefix(str, offset) { @@ -4596,8 +4336,8 @@ // verdor prefix should be at least 3 chars length if (str.length - offset >= 3) { // vendor prefix starts with hyper minus following non-hyper minus - if (str.charCodeAt(offset) === HYPHENMINUS$2 && - str.charCodeAt(offset + 1) !== HYPHENMINUS$2) { + if (str.charCodeAt(offset) === HYPHENMINUS && + str.charCodeAt(offset + 1) !== HYPHENMINUS) { // vendor prefix should contain a hyper minus at the ending var secondDashIndex = str.indexOf('-', offset + 2); @@ -4647,7 +4387,8 @@ hack !== '*' && hack !== '$' && hack !== '#' && - hack !== '+') { + hack !== '+' && + hack !== '&') { hack = ''; } @@ -4681,21 +4422,1013 @@ vendorPrefix: getVendorPrefix }; - var findIdentifierEnd$2 = utils.findIdentifierEnd; - var findNumberEnd$2 = utils.findNumberEnd; - var findDecimalNumberEnd$1 = utils.findDecimalNumberEnd; - var isHex$1 = utils.isHex; + var MIN_SIZE = 16 * 1024; + var SafeUint32Array = typeof Uint32Array !== 'undefined' ? Uint32Array : Array; // fallback on Array when TypedArray is not supported - var SYMBOL_TYPE$2 = _const.SYMBOL_TYPE; - var IDENTIFIER$2 = _const.TYPE.Identifier; - var PLUSSIGN$2 = _const.TYPE.PlusSign; - var HYPHENMINUS$3 = _const.TYPE.HyphenMinus; - var NUMBERSIGN = _const.TYPE.NumberSign; + var adoptBuffer = function adoptBuffer(buffer, size) { + if (buffer === null || buffer.length < size) { + return new SafeUint32Array(Math.max(size + 1024, MIN_SIZE)); + } - var PERCENTAGE = { - '%': true + return buffer; }; + var TYPE$2 = _const.TYPE; + + + var isNewline$1 = charCodeDefinitions.isNewline; + var isName$2 = charCodeDefinitions.isName; + var isValidEscape$2 = charCodeDefinitions.isValidEscape; + var isNumberStart$1 = charCodeDefinitions.isNumberStart; + var isIdentifierStart$1 = charCodeDefinitions.isIdentifierStart; + var charCodeCategory$1 = charCodeDefinitions.charCodeCategory; + var isBOM$1 = charCodeDefinitions.isBOM; + + + var cmpStr$2 = utils.cmpStr; + var getNewlineLength$1 = utils.getNewlineLength; + var findWhiteSpaceEnd$1 = utils.findWhiteSpaceEnd; + var consumeEscaped$1 = utils.consumeEscaped; + var consumeName$1 = utils.consumeName; + var consumeNumber$1 = utils.consumeNumber; + var consumeBadUrlRemnants$1 = utils.consumeBadUrlRemnants; + + var OFFSET_MASK$1 = 0x00FFFFFF; + var TYPE_SHIFT$1 = 24; + + function tokenize(source, stream) { + function getCharCode(offset) { + return offset < sourceLength ? source.charCodeAt(offset) : 0; + } + + // § 4.3.3. Consume a numeric token + function consumeNumericToken() { + // Consume a number and let number be the result. + offset = consumeNumber$1(source, offset); + + // If the next 3 input code points would start an identifier, then: + if (isIdentifierStart$1(getCharCode(offset), getCharCode(offset + 1), getCharCode(offset + 2))) { + // Create a with the same value and type flag as number, and a unit set initially to the empty string. + // Consume a name. Set the ’s unit to the returned value. + // Return the . + type = TYPE$2.Dimension; + offset = consumeName$1(source, offset); + return; + } + + // Otherwise, if the next input code point is U+0025 PERCENTAGE SIGN (%), consume it. + if (getCharCode(offset) === 0x0025) { + // Create a with the same value as number, and return it. + type = TYPE$2.Percentage; + offset++; + return; + } + + // Otherwise, create a with the same value and type flag as number, and return it. + type = TYPE$2.Number; + } + + // § 4.3.4. Consume an ident-like token + function consumeIdentLikeToken() { + const nameStartOffset = offset; + + // Consume a name, and let string be the result. + offset = consumeName$1(source, offset); + + // If string’s value is an ASCII case-insensitive match for "url", + // and the next input code point is U+0028 LEFT PARENTHESIS ((), consume it. + if (cmpStr$2(source, nameStartOffset, offset, 'url') && getCharCode(offset) === 0x0028) { + // While the next two input code points are whitespace, consume the next input code point. + offset = findWhiteSpaceEnd$1(source, offset + 1); + + // If the next one or two input code points are U+0022 QUOTATION MARK ("), U+0027 APOSTROPHE ('), + // or whitespace followed by U+0022 QUOTATION MARK (") or U+0027 APOSTROPHE ('), + // then create a with its value set to string and return it. + if (getCharCode(offset) === 0x0022 || + getCharCode(offset) === 0x0027) { + type = TYPE$2.Function; + offset = nameStartOffset + 4; + return; + } + + // Otherwise, consume a url token, and return it. + consumeUrlToken(); + return; + } + + // Otherwise, if the next input code point is U+0028 LEFT PARENTHESIS ((), consume it. + // Create a with its value set to string and return it. + if (getCharCode(offset) === 0x0028) { + type = TYPE$2.Function; + offset++; + return; + } + + // Otherwise, create an with its value set to string and return it. + type = TYPE$2.Ident; + } + + // § 4.3.5. Consume a string token + function consumeStringToken(endingCodePoint) { + // This algorithm may be called with an ending code point, which denotes the code point + // that ends the string. If an ending code point is not specified, + // the current input code point is used. + if (!endingCodePoint) { + endingCodePoint = getCharCode(offset++); + } + + // Initially create a with its value set to the empty string. + type = TYPE$2.String; + + // Repeatedly consume the next input code point from the stream: + for (; offset < source.length; offset++) { + var code = source.charCodeAt(offset); + + switch (charCodeCategory$1(code)) { + // ending code point + case endingCodePoint: + // Return the . + offset++; + return; + + // EOF + case charCodeCategory$1.Eof: + // This is a parse error. Return the . + return; + + // newline + case charCodeCategory$1.WhiteSpace: + if (isNewline$1(code)) { + // This is a parse error. Reconsume the current input code point, + // create a , and return it. + offset += getNewlineLength$1(source, offset, code); + type = TYPE$2.BadString; + return; + } + break; + + // U+005C REVERSE SOLIDUS (\) + case 0x005C: + // If the next input code point is EOF, do nothing. + if (offset === source.length - 1) { + break; + } + + var nextCode = getCharCode(offset + 1); + + // Otherwise, if the next input code point is a newline, consume it. + if (isNewline$1(nextCode)) { + offset += getNewlineLength$1(source, offset + 1, nextCode); + } else if (isValidEscape$2(code, nextCode)) { + // Otherwise, (the stream starts with a valid escape) consume + // an escaped code point and append the returned code point to + // the ’s value. + offset = consumeEscaped$1(source, offset) - 1; + } + break; + + // anything else + // Append the current input code point to the ’s value. + } + } + } + + // § 4.3.6. Consume a url token + // Note: This algorithm assumes that the initial "url(" has already been consumed. + // This algorithm also assumes that it’s being called to consume an "unquoted" value, like url(foo). + // A quoted value, like url("foo"), is parsed as a . Consume an ident-like token + // automatically handles this distinction; this algorithm shouldn’t be called directly otherwise. + function consumeUrlToken() { + // Initially create a with its value set to the empty string. + type = TYPE$2.Url; + + // Consume as much whitespace as possible. + offset = findWhiteSpaceEnd$1(source, offset); + + // Repeatedly consume the next input code point from the stream: + for (; offset < source.length; offset++) { + var code = source.charCodeAt(offset); + + switch (charCodeCategory$1(code)) { + // U+0029 RIGHT PARENTHESIS ()) + case 0x0029: + // Return the . + offset++; + return; + + // EOF + case charCodeCategory$1.Eof: + // This is a parse error. Return the . + return; + + // whitespace + case charCodeCategory$1.WhiteSpace: + // Consume as much whitespace as possible. + offset = findWhiteSpaceEnd$1(source, offset); + + // If the next input code point is U+0029 RIGHT PARENTHESIS ()) or EOF, + // consume it and return the + // (if EOF was encountered, this is a parse error); + if (getCharCode(offset) === 0x0029 || offset >= source.length) { + if (offset < source.length) { + offset++; + } + return; + } + + // otherwise, consume the remnants of a bad url, create a , + // and return it. + offset = consumeBadUrlRemnants$1(source, offset); + type = TYPE$2.BadUrl; + return; + + // U+0022 QUOTATION MARK (") + // U+0027 APOSTROPHE (') + // U+0028 LEFT PARENTHESIS (() + // non-printable code point + case 0x0022: + case 0x0027: + case 0x0028: + case charCodeCategory$1.NonPrintable: + // This is a parse error. Consume the remnants of a bad url, + // create a , and return it. + offset = consumeBadUrlRemnants$1(source, offset); + type = TYPE$2.BadUrl; + return; + + // U+005C REVERSE SOLIDUS (\) + case 0x005C: + // If the stream starts with a valid escape, consume an escaped code point and + // append the returned code point to the ’s value. + if (isValidEscape$2(code, getCharCode(offset + 1))) { + offset = consumeEscaped$1(source, offset) - 1; + break; + } + + // Otherwise, this is a parse error. Consume the remnants of a bad url, + // create a , and return it. + offset = consumeBadUrlRemnants$1(source, offset); + type = TYPE$2.BadUrl; + return; + + // anything else + // Append the current input code point to the ’s value. + } + } + } + + if (!stream) { + stream = new TokenStream_1(); + } + + // ensure source is a string + source = String(source || ''); + + var sourceLength = source.length; + var offsetAndType = adoptBuffer(stream.offsetAndType, sourceLength + 1); // +1 because of eof-token + var balance = adoptBuffer(stream.balance, sourceLength + 1); + var tokenCount = 0; + var start = isBOM$1(getCharCode(0)); + var offset = start; + var balanceCloseType = 0; + var balanceStart = 0; + var balancePrev = 0; + + // https://drafts.csswg.org/css-syntax-3/#consume-token + // § 4.3.1. Consume a token + while (offset < sourceLength) { + var code = source.charCodeAt(offset); + var type = 0; + + balance[tokenCount] = sourceLength; + + switch (charCodeCategory$1(code)) { + // whitespace + case charCodeCategory$1.WhiteSpace: + // Consume as much whitespace as possible. Return a . + type = TYPE$2.WhiteSpace; + offset = findWhiteSpaceEnd$1(source, offset + 1); + break; + + // U+0022 QUOTATION MARK (") + case 0x0022: + // Consume a string token and return it. + consumeStringToken(); + break; + + // U+0023 NUMBER SIGN (#) + case 0x0023: + // If the next input code point is a name code point or the next two input code points are a valid escape, then: + if (isName$2(getCharCode(offset + 1)) || isValidEscape$2(getCharCode(offset + 1), getCharCode(offset + 2))) { + // Create a . + type = TYPE$2.Hash; + + // If the next 3 input code points would start an identifier, set the ’s type flag to "id". + // if (isIdentifierStart(getCharCode(offset + 1), getCharCode(offset + 2), getCharCode(offset + 3))) { + // // TODO: set id flag + // } + + // Consume a name, and set the ’s value to the returned string. + offset = consumeName$1(source, offset + 1); + + // Return the . + } else { + // Otherwise, return a with its value set to the current input code point. + type = TYPE$2.Delim; + offset++; + } + + break; + + // U+0027 APOSTROPHE (') + case 0x0027: + // Consume a string token and return it. + consumeStringToken(); + break; + + // U+0028 LEFT PARENTHESIS (() + case 0x0028: + // Return a <(-token>. + type = TYPE$2.LeftParenthesis; + offset++; + break; + + // U+0029 RIGHT PARENTHESIS ()) + case 0x0029: + // Return a <)-token>. + type = TYPE$2.RightParenthesis; + offset++; + break; + + // U+002B PLUS SIGN (+) + case 0x002B: + // If the input stream starts with a number, ... + if (isNumberStart$1(code, getCharCode(offset + 1), getCharCode(offset + 2))) { + // ... reconsume the current input code point, consume a numeric token, and return it. + consumeNumericToken(); + } else { + // Otherwise, return a with its value set to the current input code point. + type = TYPE$2.Delim; + offset++; + } + break; + + // U+002C COMMA (,) + case 0x002C: + // Return a . + type = TYPE$2.Comma; + offset++; + break; + + // U+002D HYPHEN-MINUS (-) + case 0x002D: + // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it. + if (isNumberStart$1(code, getCharCode(offset + 1), getCharCode(offset + 2))) { + consumeNumericToken(); + } else { + // Otherwise, if the next 2 input code points are U+002D HYPHEN-MINUS U+003E GREATER-THAN SIGN (->), consume them and return a . + if (getCharCode(offset + 1) === 0x002D && + getCharCode(offset + 2) === 0x003E) { + type = TYPE$2.CDC; + offset = offset + 3; + } else { + // Otherwise, if the input stream starts with an identifier, ... + if (isIdentifierStart$1(code, getCharCode(offset + 1), getCharCode(offset + 2))) { + // ... reconsume the current input code point, consume an ident-like token, and return it. + consumeIdentLikeToken(); + } else { + // Otherwise, return a with its value set to the current input code point. + type = TYPE$2.Delim; + offset++; + } + } + } + break; + + // U+002E FULL STOP (.) + case 0x002E: + // If the input stream starts with a number, ... + if (isNumberStart$1(code, getCharCode(offset + 1), getCharCode(offset + 2))) { + // ... reconsume the current input code point, consume a numeric token, and return it. + consumeNumericToken(); + } else { + // Otherwise, return a with its value set to the current input code point. + type = TYPE$2.Delim; + offset++; + } + + break; + + // U+002F SOLIDUS (/) + case 0x002F: + // If the next two input code point are U+002F SOLIDUS (/) followed by a U+002A ASTERISK (*), + if (getCharCode(offset + 1) === 0x002A) { + // ... consume them and all following code points up to and including the first U+002A ASTERISK (*) + // followed by a U+002F SOLIDUS (/), or up to an EOF code point. + type = TYPE$2.Comment; + offset = source.indexOf('*/', offset + 2) + 2; + if (offset === 1) { + offset = source.length; + } + } else { + type = TYPE$2.Delim; + offset++; + } + break; + + // U+003A COLON (:) + case 0x003A: + // Return a . + type = TYPE$2.Colon; + offset++; + break; + + // U+003B SEMICOLON (;) + case 0x003B: + // Return a . + type = TYPE$2.Semicolon; + offset++; + break; + + // U+003C LESS-THAN SIGN (<) + case 0x003C: + // If the next 3 input code points are U+0021 EXCLAMATION MARK U+002D HYPHEN-MINUS U+002D HYPHEN-MINUS (!--), ... + if (getCharCode(offset + 1) === 0x0021 && + getCharCode(offset + 2) === 0x002D && + getCharCode(offset + 3) === 0x002D) { + // ... consume them and return a . + type = TYPE$2.CDO; + offset = offset + 4; + } else { + // Otherwise, return a with its value set to the current input code point. + type = TYPE$2.Delim; + offset++; + } + + break; + + // U+0040 COMMERCIAL AT (@) + case 0x0040: + // If the next 3 input code points would start an identifier, ... + if (isIdentifierStart$1(getCharCode(offset + 1), getCharCode(offset + 2), getCharCode(offset + 3))) { + // ... consume a name, create an with its value set to the returned value, and return it. + type = TYPE$2.AtKeyword; + offset = consumeName$1(source, offset + 1); + } else { + // Otherwise, return a with its value set to the current input code point. + type = TYPE$2.Delim; + offset++; + } + + break; + + // U+005B LEFT SQUARE BRACKET ([) + case 0x005B: + // Return a <[-token>. + type = TYPE$2.LeftSquareBracket; + offset++; + break; + + // U+005C REVERSE SOLIDUS (\) + case 0x005C: + // If the input stream starts with a valid escape, ... + if (isValidEscape$2(code, getCharCode(offset + 1))) { + // ... reconsume the current input code point, consume an ident-like token, and return it. + consumeIdentLikeToken(); + } else { + // Otherwise, this is a parse error. Return a with its value set to the current input code point. + type = TYPE$2.Delim; + offset++; + } + break; + + // U+005D RIGHT SQUARE BRACKET (]) + case 0x005D: + // Return a <]-token>. + type = TYPE$2.RightSquareBracket; + offset++; + break; + + // U+007B LEFT CURLY BRACKET ({) + case 0x007B: + // Return a <{-token>. + type = TYPE$2.LeftCurlyBracket; + offset++; + break; + + // U+007D RIGHT CURLY BRACKET (}) + case 0x007D: + // Return a <}-token>. + type = TYPE$2.RightCurlyBracket; + offset++; + break; + + // digit + case charCodeCategory$1.Digit: + // Reconsume the current input code point, consume a numeric token, and return it. + consumeNumericToken(); + break; + + // name-start code point + case charCodeCategory$1.NameStart: + // Reconsume the current input code point, consume an ident-like token, and return it. + consumeIdentLikeToken(); + break; + + // EOF + case charCodeCategory$1.Eof: + // Return an . + break; + + // anything else + default: + // Return a with its value set to the current input code point. + type = TYPE$2.Delim; + offset++; + } + + switch (type) { + case balanceCloseType: + balancePrev = balanceStart & OFFSET_MASK$1; + balanceStart = balance[balancePrev]; + balanceCloseType = balanceStart >> TYPE_SHIFT$1; + balance[tokenCount] = balancePrev; + balance[balancePrev++] = tokenCount; + for (; balancePrev < tokenCount; balancePrev++) { + if (balance[balancePrev] === sourceLength) { + balance[balancePrev] = tokenCount; + } + } + break; + + case TYPE$2.LeftParenthesis: + case TYPE$2.Function: + balance[tokenCount] = balanceStart; + balanceCloseType = TYPE$2.RightParenthesis; + balanceStart = (balanceCloseType << TYPE_SHIFT$1) | tokenCount; + break; + + case TYPE$2.LeftSquareBracket: + balance[tokenCount] = balanceStart; + balanceCloseType = TYPE$2.RightSquareBracket; + balanceStart = (balanceCloseType << TYPE_SHIFT$1) | tokenCount; + break; + + case TYPE$2.LeftCurlyBracket: + balance[tokenCount] = balanceStart; + balanceCloseType = TYPE$2.RightCurlyBracket; + balanceStart = (balanceCloseType << TYPE_SHIFT$1) | tokenCount; + break; + } + + offsetAndType[tokenCount++] = (type << TYPE_SHIFT$1) | offset; + } + + // finalize buffers + offsetAndType[tokenCount] = (TYPE$2.EOF << TYPE_SHIFT$1) | offset; // + balance[tokenCount] = sourceLength; + balance[sourceLength] = sourceLength; // prevents false positive balance match with any token + while (balanceStart !== 0) { + balancePrev = balanceStart & OFFSET_MASK$1; + balanceStart = balance[balancePrev]; + balance[balancePrev] = sourceLength; + } + + // update stream + stream.source = source; + stream.firstCharOffset = start; + stream.offsetAndType = offsetAndType; + stream.tokenCount = tokenCount; + stream.balance = balance; + stream.reset(); + stream.next(); + + return stream; + } + + // extend tokenizer with constants + Object.keys(_const).forEach(function(key) { + tokenize[key] = _const[key]; + }); + + // extend tokenizer with static methods from utils + Object.keys(charCodeDefinitions).forEach(function(key) { + tokenize[key] = charCodeDefinitions[key]; + }); + Object.keys(utils).forEach(function(key) { + tokenize[key] = utils[key]; + }); + + var tokenizer = tokenize; + + var isDigit$2 = tokenizer.isDigit; + var cmpChar$1 = tokenizer.cmpChar; + var TYPE$3 = tokenizer.TYPE; + + var DELIM = TYPE$3.Delim; + var WHITESPACE$1 = TYPE$3.WhiteSpace; + var COMMENT$1 = TYPE$3.Comment; + var IDENT = TYPE$3.Ident; + var NUMBER = TYPE$3.Number; + var DIMENSION = TYPE$3.Dimension; + var PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+) + var HYPHENMINUS$1 = 0x002D; // U+002D HYPHEN-MINUS (-) + var N = 0x006E; // U+006E LATIN SMALL LETTER N (n) + var DISALLOW_SIGN = true; + var ALLOW_SIGN = false; + + function isDelim(token, code) { + return token !== null && token.type === DELIM && token.value.charCodeAt(0) === code; + } + + function skipSC(token, offset, getNextToken) { + while (token !== null && (token.type === WHITESPACE$1 || token.type === COMMENT$1)) { + token = getNextToken(++offset); + } + + return offset; + } + + function checkInteger(token, valueOffset, disallowSign, offset) { + if (!token) { + return 0; + } + + var code = token.value.charCodeAt(valueOffset); + + if (code === PLUSSIGN || code === HYPHENMINUS$1) { + if (disallowSign) { + // Number sign is not allowed + return 0; + } + valueOffset++; + } + + for (; valueOffset < token.value.length; valueOffset++) { + if (!isDigit$2(token.value.charCodeAt(valueOffset))) { + // Integer is expected + return 0; + } + } + + return offset + 1; + } + + // ... + // ... ['+' | '-'] + function consumeB(token, offset_, getNextToken) { + var sign = false; + var offset = skipSC(token, offset_, getNextToken); + + token = getNextToken(offset); + + if (token === null) { + return offset_; + } + + if (token.type !== NUMBER) { + if (isDelim(token, PLUSSIGN) || isDelim(token, HYPHENMINUS$1)) { + sign = true; + offset = skipSC(getNextToken(++offset), offset, getNextToken); + token = getNextToken(offset); + + if (token === null && token.type !== NUMBER) { + return 0; + } + } else { + return offset_; + } + } + + if (!sign) { + var code = token.value.charCodeAt(0); + if (code !== PLUSSIGN && code !== HYPHENMINUS$1) { + // Number sign is expected + return 0; + } + } + + return checkInteger(token, sign ? 0 : 1, sign, offset); + } + + // An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb + var genericAnPlusB = function anPlusB(token, getNextToken) { + /* eslint-disable brace-style*/ + var offset = 0; + + if (!token) { + return 0; + } + + // + if (token.type === NUMBER) { + return checkInteger(token, 0, ALLOW_SIGN, offset); // b + } + + // -n + // -n + // -n ['+' | '-'] + // -n- + // + else if (token.type === IDENT && token.value.charCodeAt(0) === HYPHENMINUS$1) { + // expect 1st char is N + if (!cmpChar$1(token.value, 1, N)) { + return 0; + } + + switch (token.value.length) { + // -n + // -n + // -n ['+' | '-'] + case 2: + return consumeB(getNextToken(++offset), offset, getNextToken); + + // -n- + case 3: + if (token.value.charCodeAt(2) !== HYPHENMINUS$1) { + return 0; + } + + offset = skipSC(getNextToken(++offset), offset, getNextToken); + token = getNextToken(offset); + + return checkInteger(token, 0, DISALLOW_SIGN, offset); + + // + default: + if (token.value.charCodeAt(2) !== HYPHENMINUS$1) { + return 0; + } + + return checkInteger(token, 3, DISALLOW_SIGN, offset); + } + } + + // '+'? n + // '+'? n + // '+'? n ['+' | '-'] + // '+'? n- + // '+'? + else if (token.type === IDENT || (isDelim(token, PLUSSIGN) && getNextToken(offset + 1).type === IDENT)) { + // just ignore a plus + if (token.type !== IDENT) { + token = getNextToken(++offset); + } + + if (token === null || !cmpChar$1(token.value, 0, N)) { + return 0; + } + + switch (token.value.length) { + // '+'? n + // '+'? n + // '+'? n ['+' | '-'] + case 1: + return consumeB(getNextToken(++offset), offset, getNextToken); + + // '+'? n- + case 2: + if (token.value.charCodeAt(1) !== HYPHENMINUS$1) { + return 0; + } + + offset = skipSC(getNextToken(++offset), offset, getNextToken); + token = getNextToken(offset); + + return checkInteger(token, 0, DISALLOW_SIGN, offset); + + // '+'? + default: + if (token.value.charCodeAt(1) !== HYPHENMINUS$1) { + return 0; + } + + return checkInteger(token, 2, DISALLOW_SIGN, offset); + } + } + + // + // + // + // + // ['+' | '-'] + else if (token.type === DIMENSION) { + var code = token.value.charCodeAt(0); + var sign = code === PLUSSIGN || code === HYPHENMINUS$1 ? 1 : 0; + + for (var i = sign; i < token.value.length; i++) { + if (!isDigit$2(token.value.charCodeAt(i))) { + break; + } + } + + if (i === sign) { + // Integer is expected + return 0; + } + + if (!cmpChar$1(token.value, i, N)) { + return 0; + } + + // + // + // ['+' | '-'] + if (i + 1 === token.value.length) { + return consumeB(getNextToken(++offset), offset, getNextToken); + } else { + if (token.value.charCodeAt(i + 1) !== HYPHENMINUS$1) { + return 0; + } + + // + if (i + 2 === token.value.length) { + offset = skipSC(getNextToken(++offset), offset, getNextToken); + token = getNextToken(offset); + + return checkInteger(token, 0, DISALLOW_SIGN, offset); + } + // + else { + return checkInteger(token, i + 2, DISALLOW_SIGN, offset); + } + } + } + + return 0; + }; + + var isHexDigit$2 = tokenizer.isHexDigit; + var cmpChar$2 = tokenizer.cmpChar; + var TYPE$4 = tokenizer.TYPE; + + var IDENT$1 = TYPE$4.Ident; + var DELIM$1 = TYPE$4.Delim; + var NUMBER$1 = TYPE$4.Number; + var DIMENSION$1 = TYPE$4.Dimension; + var PLUSSIGN$1 = 0x002B; // U+002B PLUS SIGN (+) + var HYPHENMINUS$2 = 0x002D; // U+002D HYPHEN-MINUS (-) + var QUESTIONMARK = 0x003F; // U+003F QUESTION MARK (?) + var U = 0x0075; // U+0075 LATIN SMALL LETTER U (u) + + function isDelim$1(token, code) { + return token !== null && token.type === DELIM$1 && token.value.charCodeAt(0) === code; + } + + function startsWith(token, code) { + return token.value.charCodeAt(0) === code; + } + + function hexSequence(token, offset, allowDash) { + for (var pos = offset, hexlen = 0; pos < token.value.length; pos++) { + var code = token.value.charCodeAt(pos); + + if (code === HYPHENMINUS$2 && allowDash && hexlen !== 0) { + if (hexSequence(token, offset + hexlen + 1, false) > 0) { + return 6; // dissallow following question marks + } + + return 0; // dash at the ending of a hex sequence is not allowed + } + + if (!isHexDigit$2(code)) { + return 0; // not a hex digit + } + + if (++hexlen > 6) { + return 0; // too many hex digits + } } + + return hexlen; + } + + function withQuestionMarkSequence(consumed, length, getNextToken) { + if (!consumed) { + return 0; // nothing consumed + } + + while (isDelim$1(getNextToken(length), QUESTIONMARK)) { + if (++consumed > 6) { + return 0; // too many question marks + } + + length++; + } + + return length; + } + + // https://drafts.csswg.org/css-syntax/#urange + // Informally, the production has three forms: + // U+0001 + // Defines a range consisting of a single code point, in this case the code point "1". + // U+0001-00ff + // Defines a range of codepoints between the first and the second value, in this case + // the range between "1" and "ff" (255 in decimal) inclusive. + // U+00?? + // Defines a range of codepoints where the "?" characters range over all hex digits, + // in this case defining the same as the value U+0000-00ff. + // In each form, a maximum of 6 digits is allowed for each hexadecimal number (if you treat "?" as a hexadecimal digit). + // + // = + // u '+' '?'* | + // u '?'* | + // u '?'* | + // u | + // u | + // u '+' '?'+ + var genericUrange = function urange(token, getNextToken) { + var length = 0; + + // should start with `u` or `U` + if (token === null || token.type !== IDENT$1 || !cmpChar$2(token.value, 0, U)) { + return 0; + } + + token = getNextToken(++length); + if (token === null) { + return 0; + } + + // u '+' '?'* + // u '+' '?'+ + if (isDelim$1(token, PLUSSIGN$1)) { + token = getNextToken(++length); + if (token === null) { + return 0; + } + + if (token.type === IDENT$1) { + // u '+' '?'* + return withQuestionMarkSequence(hexSequence(token, 0, true), ++length, getNextToken); + } + + if (isDelim$1(token, QUESTIONMARK)) { + // u '+' '?'+ + return withQuestionMarkSequence(1, ++length, getNextToken); + } + + // Hex digit or question mark is expected + return 0; + } + + // u '?'* + // u + // u + if (token.type === NUMBER$1) { + if (!startsWith(token, PLUSSIGN$1)) { + return 0; + } + + var consumedHexLength = hexSequence(token, 1, true); + if (consumedHexLength === 0) { + return 0; + } + + token = getNextToken(++length); + if (token === null) { + // u + return length; + } + + if (token.type === DIMENSION$1 || token.type === NUMBER$1) { + // u + // u + if (!startsWith(token, HYPHENMINUS$2) || !hexSequence(token, 1, false)) { + return 0; + } + + return length + 1; + } + + // u '?'* + return withQuestionMarkSequence(consumedHexLength, length, getNextToken); + } + + // u '?'* + if (token.type === DIMENSION$1) { + if (!startsWith(token, PLUSSIGN$1)) { + return 0; + } + + return withQuestionMarkSequence(hexSequence(token, 1, true), ++length, getNextToken); + } + + return 0; + }; + + var isIdentifierStart$2 = tokenizer.isIdentifierStart; + var isHexDigit$3 = tokenizer.isHexDigit; + var isDigit$3 = tokenizer.isDigit; + var cmpStr$3 = tokenizer.cmpStr; + var consumeNumber$2 = tokenizer.consumeNumber; + var TYPE$5 = tokenizer.TYPE; + + + + var cssWideKeywords = ['unset', 'initial', 'inherit']; + var calcFunctionNames = ['calc(', '-moz-calc(', '-webkit-calc(']; + // https://www.w3.org/TR/css-values-3/#lengths var LENGTH = { // absolute length units @@ -4761,343 +5494,516 @@ 'st': true }; - function consumeFunction(token, addTokenToMatch, getNextToken) { - var length = 1; - var cursor; + // safe char code getter + function charCode(str, index) { + return index < str.length ? str.charCodeAt(index) : 0; + } - do { - cursor = getNextToken(length++); - } while (cursor !== null && cursor.node !== token.node); + function eqStr(actual, expected) { + return cmpStr$3(actual, 0, actual.length, expected); + } - if (cursor === null) { - return false; - } - - while (true) { - // consume tokens until cursor - if (addTokenToMatch() === cursor) { - break; + function eqStrAny(actual, expected) { + for (var i = 0; i < expected.length; i++) { + if (eqStr(actual, expected[i])) { + return true; } } - return true; + return false; + } + + // IE postfix hack, i.e. 123\0 or 123px\9 + function isPostfixIeHack(str, offset) { + if (offset !== str.length - 2) { + return false; + } + + return ( + str.charCodeAt(offset) === 0x005C && // U+005C REVERSE SOLIDUS (\) + isDigit$3(str.charCodeAt(offset + 1)) + ); + } + + function outOfRange(opts, value, numEnd) { + if (opts && opts.type === 'Range') { + var num = Number( + numEnd !== undefined && numEnd !== value.length + ? value.substr(0, numEnd) + : value + ); + + if (isNaN(num)) { + return true; + } + + if (opts.min !== null && num < opts.min) { + return true; + } + + if (opts.max !== null && num > opts.max) { + return true; + } + } + + return false; + } + + function consumeFunction(token, getNextToken) { + var startIdx = token.index; + var length = 0; + + // balanced token consuming + do { + length++; + + if (token.balance <= startIdx) { + break; + } + } while (token = getNextToken(length)); + + return length; } // TODO: implement // can be used wherever , , ,