(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.csso = {})); }(this, function (exports) { 'use strict'; // // list // ┌──────┐ // ┌──────────────┼─head │ // │ │ tail─┼──────────────┐ // │ └──────┘ │ // ▼ ▼ // item item item item // ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ // null ◀──┼─prev │◀───┼─prev │◀───┼─prev │◀───┼─prev │ // │ next─┼───▶│ next─┼───▶│ next─┼───▶│ next─┼──▶ null // ├──────┤ ├──────┤ ├──────┤ ├──────┤ // │ data │ │ data │ │ data │ │ data │ // └──────┘ └──────┘ └──────┘ └──────┘ // function createItem(data) { return { prev: null, next: null, data: data }; } function allocateCursor(node, prev, next) { var cursor; if (cursors !== null) { cursor = cursors; cursors = cursors.cursor; cursor.prev = prev; cursor.next = next; cursor.cursor = node.cursor; } else { cursor = { prev: prev, next: next, cursor: node.cursor }; } node.cursor = cursor; return cursor; } function releaseCursor(node) { var cursor = node.cursor; node.cursor = cursor.cursor; cursor.prev = null; cursor.next = null; cursor.cursor = cursors; cursors = cursor; } var cursors = null; var List = function() { this.cursor = null; this.head = null; this.tail = null; }; List.createItem = createItem; List.prototype.createItem = createItem; List.prototype.updateCursors = function(prevOld, prevNew, nextOld, nextNew) { var cursor = this.cursor; while (cursor !== null) { if (cursor.prev === prevOld) { cursor.prev = prevNew; } if (cursor.next === nextOld) { cursor.next = nextNew; } cursor = cursor.cursor; } }; List.prototype.getSize = function() { var size = 0; var cursor = this.head; while (cursor) { size++; cursor = cursor.next; } return size; }; List.prototype.fromArray = function(array) { var cursor = null; this.head = null; for (var i = 0; i < array.length; i++) { var item = createItem(array[i]); if (cursor !== null) { cursor.next = item; } else { this.head = item; } item.prev = cursor; cursor = item; } this.tail = cursor; return this; }; List.prototype.toArray = function() { var cursor = this.head; var result = []; while (cursor) { result.push(cursor.data); cursor = cursor.next; } return result; }; List.prototype.toJSON = List.prototype.toArray; List.prototype.isEmpty = function() { return this.head === null; }; List.prototype.first = function() { return this.head && this.head.data; }; List.prototype.last = function() { return this.tail && this.tail.data; }; List.prototype.each = function(fn, context) { var item; if (context === undefined) { context = this; } // push cursor var cursor = allocateCursor(this, null, this.head); while (cursor.next !== null) { item = cursor.next; cursor.next = item.next; fn.call(context, item.data, item, this); } // pop cursor releaseCursor(this); }; List.prototype.forEach = List.prototype.each; List.prototype.eachRight = function(fn, context) { var item; if (context === undefined) { context = this; } // push cursor var cursor = allocateCursor(this, this.tail, null); while (cursor.prev !== null) { item = cursor.prev; cursor.prev = item.prev; fn.call(context, item.data, item, this); } // pop cursor releaseCursor(this); }; List.prototype.forEachRight = List.prototype.eachRight; List.prototype.nextUntil = function(start, fn, context) { if (start === null) { return; } var item; if (context === undefined) { context = this; } // push cursor var cursor = allocateCursor(this, null, start); while (cursor.next !== null) { item = cursor.next; cursor.next = item.next; if (fn.call(context, item.data, item, this)) { break; } } // pop cursor releaseCursor(this); }; List.prototype.prevUntil = function(start, fn, context) { if (start === null) { return; } var item; if (context === undefined) { context = this; } // push cursor var cursor = allocateCursor(this, start, null); while (cursor.prev !== null) { item = cursor.prev; cursor.prev = item.prev; if (fn.call(context, item.data, item, this)) { break; } } // pop cursor releaseCursor(this); }; List.prototype.some = function(fn, context) { var cursor = this.head; if (context === undefined) { context = this; } while (cursor !== null) { if (fn.call(context, cursor.data, cursor, this)) { return true; } cursor = cursor.next; } return false; }; List.prototype.map = function(fn, context) { var result = new List(); var cursor = this.head; if (context === undefined) { context = this; } while (cursor !== null) { result.appendData(fn.call(context, cursor.data, cursor, this)); cursor = cursor.next; } return result; }; List.prototype.filter = function(fn, context) { var result = new List(); var cursor = this.head; if (context === undefined) { context = this; } while (cursor !== null) { if (fn.call(context, cursor.data, cursor, this)) { result.appendData(cursor.data); } cursor = cursor.next; } return result; }; List.prototype.clear = function() { this.head = null; this.tail = null; }; List.prototype.copy = function() { var result = new List(); var cursor = this.head; while (cursor !== null) { result.insert(createItem(cursor.data)); cursor = cursor.next; } return result; }; List.prototype.prepend = function(item) { // head // ^ // item this.updateCursors(null, item, this.head, item); // insert to the beginning of the list if (this.head !== null) { // new item <- first item this.head.prev = item; // new item -> first item item.next = this.head; } else { // if list has no head, then it also has no tail // in this case tail points to the new item this.tail = item; } // head always points to new item this.head = item; return this; }; List.prototype.prependData = function(data) { return this.prepend(createItem(data)); }; List.prototype.append = function(item) { return this.insert(item); }; List.prototype.appendData = function(data) { return this.insert(createItem(data)); }; List.prototype.insert = function(item, before) { if (before !== undefined && before !== null) { // prev before // ^ // item this.updateCursors(before.prev, item, before, item); if (before.prev === null) { // insert to the beginning of list if (this.head !== before) { throw new Error('before doesn\'t belong to list'); } // since head points to before therefore list doesn't empty // no need to check tail this.head = item; before.prev = item; item.next = before; this.updateCursors(null, item); } else { // insert between two items before.prev.next = item; item.prev = before.prev; before.prev = item; item.next = before; } } else { // tail // ^ // item this.updateCursors(this.tail, item, null, item); // insert to the ending of the list if (this.tail !== null) { // last item -> new item this.tail.next = item; // last item <- new item item.prev = this.tail; } else { // if list has no tail, then it also has no head // in this case head points to new item this.head = item; } // tail always points to new item this.tail = item; } return this; }; List.prototype.insertData = function(data, before) { return this.insert(createItem(data), before); }; List.prototype.remove = function(item) { // item // ^ // prev next this.updateCursors(item, item.prev, item, item.next); if (item.prev !== null) { item.prev.next = item.next; } else { if (this.head !== item) { throw new Error('item doesn\'t belong to list'); } this.head = item.next; } if (item.next !== null) { item.next.prev = item.prev; } else { if (this.tail !== item) { throw new Error('item doesn\'t belong to list'); } this.tail = item.prev; } item.prev = null; item.next = null; return item; }; List.prototype.push = function(data) { this.insert(createItem(data)); }; List.prototype.pop = function() { if (this.tail !== null) { return this.remove(this.tail); } }; List.prototype.unshift = function(data) { this.prepend(createItem(data)); }; List.prototype.shift = function() { if (this.head !== null) { return this.remove(this.head); } }; List.prototype.prependList = function(list) { return this.insertList(list, this.head); }; List.prototype.appendList = function(list) { return this.insertList(list); }; List.prototype.insertList = function(list, before) { // ignore empty lists if (list.head === null) { return this; } if (before !== undefined && before !== null) { this.updateCursors(before.prev, list.tail, before, list.head); // insert in the middle of dist list if (before.prev !== null) { // before.prev <-> list.head before.prev.next = list.head; list.head.prev = before.prev; } else { this.head = list.head; } before.prev = list.tail; list.tail.next = before; } else { this.updateCursors(this.tail, list.tail, null, list.head); // insert to end of the list if (this.tail !== null) { // if destination list has a tail, then it also has a head, // but head doesn't change // dest tail -> source head this.tail.next = list.head; // dest tail <- source head list.head.prev = this.tail; } else { // if list has no a tail, then it also has no a head // in this case points head to new item this.head = list.head; } // tail always start point to new item this.tail = list.tail; } list.head = null; list.tail = null; return this; }; List.prototype.replace = function(oldItem, newItemOrList) { if ('head' in newItemOrList) { this.insertList(newItemOrList, oldItem); } else { this.insert(newItemOrList, oldItem); } this.remove(oldItem); }; var List_1 = List; var createCustomError = function createCustomError(name, message) { // use Object.create(), because some VMs prevent setting line/column otherwise // (iOS Safari 10 even throws an exception) var error = Object.create(SyntaxError.prototype); var errorStack = new Error(); error.name = name; error.message = message; Object.defineProperty(error, 'stack', { get: function() { return (errorStack.stack || '').replace(/^(.+\n){1,3}/, name + ': ' + message + '\n'); } }); return error; }; var MAX_LINE_LENGTH = 100; var OFFSET_CORRECTION = 60; var TAB_REPLACEMENT = ' '; function sourceFragment(error, extraLines) { function processLines(start, end) { return lines.slice(start, end).map(function(line, idx) { var num = String(start + idx + 1); while (num.length < maxNumLength) { num = ' ' + num; } return num + ' |' + line; }).join('\n'); } var lines = error.source.split(/\r\n?|\n|\f/); var line = error.line; var column = error.column; var startLine = Math.max(1, line - extraLines) - 1; var endLine = Math.min(line + extraLines, lines.length + 1); var maxNumLength = Math.max(4, String(endLine).length) + 1; var cutLeft = 0; // column correction according to replaced tab before column column += (TAB_REPLACEMENT.length - 1) * (lines[line - 1].substr(0, column - 1).match(/\t/g) || []).length; if (column > MAX_LINE_LENGTH) { cutLeft = column - OFFSET_CORRECTION + 3; column = OFFSET_CORRECTION - 2; } for (var i = startLine; i <= endLine; i++) { if (i >= 0 && i < lines.length) { lines[i] = lines[i].replace(/\t/g, TAB_REPLACEMENT); lines[i] = (cutLeft > 0 && lines[i].length > cutLeft ? '\u2026' : '') + lines[i].substr(cutLeft, MAX_LINE_LENGTH - 2) + (lines[i].length > cutLeft + MAX_LINE_LENGTH - 1 ? '\u2026' : ''); } } return [ processLines(startLine, line), new Array(column + maxNumLength + 2).join('-') + '^', processLines(line, endLine) ].filter(Boolean).join('\n'); } var SyntaxError$1 = function(message, source, offset, line, column) { var error = createCustomError('SyntaxError', message); error.source = source; error.offset = offset; error.line = line; error.column = column; error.sourceFragment = function(extraLines) { return sourceFragment(error, isNaN(extraLines) ? 0 : extraLines); }; Object.defineProperty(error, 'formattedMessage', { get: function() { return ( 'Parse error: ' + error.message + '\n' + sourceFragment(error, 2) ); } }); // for backward capability error.parseError = { offset: offset, line: line, column: column }; return error; }; var _SyntaxError = SyntaxError$1; // CSS Syntax Module Level 3 // https://www.w3.org/TR/css-syntax-3/ var TYPE = { 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) { result[TYPE[key]] = key; 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 // > name-start code point // > 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 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 < 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; } } 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, isBOM: isBOM, charCodeCategory: charCodeCategory }; 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 getCharCode(source, offset) { return offset < source.length ? source.charCodeAt(offset) : 0; } function getNewlineLength(source, offset, code) { if (code === 13 /* \r */ && getCharCode(source, offset + 1) === 10 /* \n */) { return 2; } return 1; } function cmpChar(testStr, offset, referenceCode) { var code = testStr.charCodeAt(offset); // code.toLowerCase() for A..Z if (isUppercaseLetter$1(code)) { code = code | 32; } return code === referenceCode; } function cmpStr(testStr, start, end, referenceStr) { if (end - start !== referenceStr.length) { return false; } if (start < 0 || end > testStr.length) { return false; } for (var i = start; i < end; i++) { var testCode = testStr.charCodeAt(i); var referenceCode = referenceStr.charCodeAt(i - start); // testCode.toLowerCase() for A..Z if (isUppercaseLetter$1(testCode)) { testCode = testCode | 32; } if (testCode !== referenceCode) { return false; } } return true; } function findWhiteSpaceStart(source, offset) { for (; offset >= 0; offset--) { if (!isWhiteSpace$1(source.charCodeAt(offset))) { break; } } return offset + 1; } function findWhiteSpaceEnd(source, offset) { for (; offset < source.length; offset++) { if (!isWhiteSpace$1(source.charCodeAt(offset))) { break; } } return offset; } function findDecimalNumberEnd(source, offset) { for (; offset < source.length; offset++) { if (!isDigit$1(source.charCodeAt(offset))) { break; } } return offset; } // § 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; // 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; } // 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; } // §4.3.12. Consume a number function consumeNumber(source, offset) { var code = source.charCodeAt(offset); // 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; } // § 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); // 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 = { consumeEscaped: consumeEscaped, consumeName: consumeName, consumeNumber: consumeNumber, consumeBadUrlRemnants: consumeBadUrlRemnants, cmpChar: cmpChar, cmpStr: cmpStr, getNewlineLength: getNewlineLength, findWhiteSpaceStart: findWhiteSpaceStart, findWhiteSpaceEnd: findWhiteSpaceEnd }; var TYPE$1 = _const.TYPE; var NAME$1 = _const.NAME; var cmpStr$1 = utils.cmpStr; var EOF$1 = TYPE$1.EOF; var WHITESPACE = TYPE$1.WhiteSpace; var COMMENT = TYPE$1.Comment; var OFFSET_MASK = 0x00FFFFFF; var TYPE_SHIFT = 24; var TokenStream = function() { this.offsetAndType = null; this.balance = null; this.reset(); }; TokenStream.prototype = { reset: function() { this.eof = false; this.tokenIndex = -1; this.tokenType = 0; this.tokenStart = this.firstCharOffset; this.tokenEnd = this.firstCharOffset; }, lookupType: function(offset) { offset += this.tokenIndex; if (offset < this.tokenCount) { return this.offsetAndType[offset] >> TYPE_SHIFT; } return EOF$1; }, lookupOffset: function(offset) { offset += this.tokenIndex; if (offset < this.tokenCount) { return this.offsetAndType[offset - 1] & OFFSET_MASK; } return this.source.length; }, lookupValue: function(offset, referenceStr) { offset += this.tokenIndex; if (offset < this.tokenCount) { return cmpStr$1( this.source, this.offsetAndType[offset - 1] & OFFSET_MASK, this.offsetAndType[offset] & OFFSET_MASK, referenceStr ); } return false; }, getTokenStart: function(tokenIndex) { if (tokenIndex === this.tokenIndex) { return this.tokenStart; } if (tokenIndex > 0) { return tokenIndex < this.tokenCount ? this.offsetAndType[tokenIndex - 1] & OFFSET_MASK : this.offsetAndType[this.tokenCount] & OFFSET_MASK; } return this.firstCharOffset; }, // 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]; // 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 (mode(type, this.source, offset)) { case 1: break loop; 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.tokenIndex; }, isBalanceEdge: function(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.tokenIndex, skipTokenCount = 0; i < this.tokenCount; i++, skipTokenCount++) { if ((this.offsetAndType[i] >> TYPE_SHIFT) !== WHITESPACE) { break; } } if (skipTokenCount > 0) { this.skip(skipTokenCount); } }, skipSC: function() { while (this.tokenType === WHITESPACE || this.tokenType === COMMENT) { this.next(); } }, skip: function(tokenCount) { var next = this.tokenIndex + tokenCount; if (next < this.tokenCount) { 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.tokenIndex = this.tokenCount; this.next(); } }, next: function() { var next = this.tokenIndex + 1; if (next < this.tokenCount) { this.tokenIndex = next; this.tokenStart = this.tokenEnd; next = this.offsetAndType[next]; this.tokenType = next >> TYPE_SHIFT; this.tokenEnd = next & OFFSET_MASK; } else { this.tokenIndex = this.tokenCount; this.eof = true; this.tokenType = EOF$1; this.tokenStart = this.tokenEnd = this.source.length; } }, dump: function() { var offset = this.firstCharOffset; return Array.prototype.slice.call(this.offsetAndType, 0, this.tokenCount).map(function(item, idx) { var start = offset; var end = item & OFFSET_MASK; offset = end; return { idx: idx, type: NAME$1[item >> TYPE_SHIFT], chunk: this.source.substring(start, end), balance: this.balance[idx] }; }, this); } }; var TokenStream_1 = TokenStream; function noop(value) { return value; } function generateMultiplier(multiplier) { if (multiplier.min === 0 && multiplier.max === 0) { return '*'; } if (multiplier.min === 0 && multiplier.max === 1) { return '?'; } if (multiplier.min === 1 && multiplier.max === 0) { return multiplier.comma ? '#' : '+'; } if (multiplier.min === 1 && multiplier.max === 1) { return ''; } return ( (multiplier.comma ? '#' : '') + (multiplier.min === multiplier.max ? '{' + multiplier.min + '}' : '{' + multiplier.min + ',' + (multiplier.max !== 0 ? multiplier.max : '') + '}' ) ); } 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, decorate, forceBraces, compact); }).join(combinator); if (node.explicit || forceBraces) { result = (compact || result[0] === ',' ? '[' : '[ ') + result + (compact ? ']' : ' ]'); } return result; } function generate(node, decorate, forceBraces, compact) { var result; switch (node.type) { case 'Group': result = generateSequence(node, decorate, forceBraces, compact) + (node.disallowEmpty ? '!' : ''); break; case 'Multiplier': // return since node is a composition return ( generate(node.term, decorate, forceBraces, compact) + decorate(generateMultiplier(node), node) ); case 'Type': result = '<' + node.name + (node.opts ? decorate(generateTypeOpts(node.opts), node.opts) : '') + '>'; break; case 'Property': result = '<\'' + node.name + '\'>'; break; case 'Keyword': result = node.name; break; case 'AtKeyword': result = '@' + node.name; break; case 'Function': result = node.name + '('; break; case 'String': case 'Token': result = node.value; break; case 'Comma': result = ','; break; default: throw new Error('Unknown node type `' + node.type + '`'); } return decorate(result, node); } var generate_1 = function(node, options) { var decorate = noop; 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, 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 = -1; var entries = 0; var css = ''; for (var i = 0; i < tokens.length; i++) { if (i === longestMatch) { mismatchOffset = css.length; } if (node !== null && tokens[i].node === node) { if (i <= longestMatch) { entries++; } else { entries = 0; } } css += tokens[i].value; } return { node: node, css: css, mismatchOffset: mismatchOffset === -1 ? css.length : mismatchOffset, last: node === null || entries > 1 }; } function getLocation(node, point) { var loc = node && node.loc && node.loc[point]; if (loc) { return { offset: loc.offset, line: loc.line, column: loc.column }; } return null; } var SyntaxReferenceError = function(type, referenceName) { var error = createCustomError( 'SyntaxReferenceError', type + (referenceName ? ' `' + referenceName + '`' : '') ); error.reference = referenceName; return error; }; var MatchError = function(message, syntax, node, matchResult) { var error = createCustomError('SyntaxMatchError', message); var details = fromMatchResult(matchResult); var mismatchOffset = details.mismatchOffset || 0; var badNode = details.node || node; var end = getLocation(badNode, 'end'); var start = details.last ? end : getLocation(badNode, 'start'); var css = details.css; error.rawMessage = message; error.syntax = syntax ? generate_1(syntax) : ''; error.css = css; error.mismatchOffset = mismatchOffset; error.loc = { source: (badNode && badNode.loc && badNode.loc.source) || '', start: start, end: end }; error.line = start ? start.line : undefined; error.column = start ? start.column : undefined; error.offset = start ? start.offset : undefined; error.message = message + '\n' + ' syntax: ' + error.syntax + '\n' + ' value: ' + (error.css || '') + '\n' + ' --------' + new Array(error.mismatchOffset + 1).join('-') + '^'; return error; }; var error = { SyntaxReferenceError: SyntaxReferenceError, MatchError: MatchError }; var hasOwnProperty = Object.prototype.hasOwnProperty; var keywords = Object.create(null); var properties = Object.create(null); var HYPHENMINUS = 45; // '-'.charCodeAt() function isCustomProperty(str, offset) { offset = offset || 0; return str.length - offset >= 2 && str.charCodeAt(offset) === HYPHENMINUS && str.charCodeAt(offset + 1) === HYPHENMINUS; } function getVendorPrefix(str, offset) { offset = offset || 0; // 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 && str.charCodeAt(offset + 1) !== HYPHENMINUS) { // vendor prefix should contain a hyper minus at the ending var secondDashIndex = str.indexOf('-', offset + 2); if (secondDashIndex !== -1) { return str.substring(offset, secondDashIndex + 1); } } } return ''; } function getKeywordDescriptor(keyword) { if (hasOwnProperty.call(keywords, keyword)) { return keywords[keyword]; } var name = keyword.toLowerCase(); if (hasOwnProperty.call(keywords, name)) { return keywords[keyword] = keywords[name]; } var custom = isCustomProperty(name, 0); var vendor = !custom ? getVendorPrefix(name, 0) : ''; return keywords[keyword] = Object.freeze({ basename: name.substr(vendor.length), name: name, vendor: vendor, prefix: vendor, custom: custom }); } function getPropertyDescriptor(property) { if (hasOwnProperty.call(properties, property)) { return properties[property]; } var name = property; var hack = property[0]; if (hack === '/') { hack = property[1] === '/' ? '//' : '/'; } else if (hack !== '_' && hack !== '*' && hack !== '$' && hack !== '#' && hack !== '+' && hack !== '&') { hack = ''; } var custom = isCustomProperty(name, hack.length); // re-use result when possible (the same as for lower case) if (!custom) { name = name.toLowerCase(); if (hasOwnProperty.call(properties, name)) { return properties[property] = properties[name]; } } var vendor = !custom ? getVendorPrefix(name, hack.length) : ''; var prefix = name.substr(0, hack.length + vendor.length); return properties[property] = Object.freeze({ basename: name.substr(prefix.length), name: name.substr(hack.length), hack: hack, vendor: vendor, prefix: prefix, custom: custom }); } var names = { keyword: getKeywordDescriptor, property: getPropertyDescriptor, isCustomProperty: isCustomProperty, vendorPrefix: getVendorPrefix }; var MIN_SIZE = 16 * 1024; var SafeUint32Array = typeof Uint32Array !== 'undefined' ? Uint32Array : Array; // fallback on Array when TypedArray is not supported var adoptBuffer = function adoptBuffer(buffer, size) { if (buffer === null || buffer.length < size) { return new SafeUint32Array(Math.max(size + 1024, MIN_SIZE)); } 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 'px': true, 'mm': true, 'cm': true, 'in': true, 'pt': true, 'pc': true, 'q': true, // relative length units 'em': true, 'ex': true, 'ch': true, 'rem': true, // viewport-percentage lengths 'vh': true, 'vw': true, 'vmin': true, 'vmax': true, 'vm': true }; var ANGLE = { 'deg': true, 'grad': true, 'rad': true, 'turn': true }; var TIME = { 's': true, 'ms': true }; var FREQUENCY = { 'hz': true, 'khz': true }; // https://www.w3.org/TR/css-values-3/#resolution (https://drafts.csswg.org/css-values/#resolution) var RESOLUTION = { 'dpi': true, 'dpcm': true, 'dppx': true, 'x': true // https://github.com/w3c/csswg-drafts/issues/461 }; // https://drafts.csswg.org/css-grid/#fr-unit var FLEX = { 'fr': true }; // https://www.w3.org/TR/css3-speech/#mixing-props-voice-volume var DECIBEL = { 'db': true }; // https://www.w3.org/TR/css3-speech/#voice-props-voice-pitch var SEMITONES = { 'st': true }; // safe char code getter function charCode(str, index) { return index < str.length ? str.charCodeAt(index) : 0; } function eqStr(actual, expected) { return cmpStr$3(actual, 0, actual.length, expected); } function eqStrAny(actual, expected) { for (var i = 0; i < expected.length; i++) { if (eqStr(actual, expected[i])) { 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 , , ,