19e815959Sopenharmony_ciimport { 29e815959Sopenharmony_ci Selector, 39e815959Sopenharmony_ci SelectorType, 49e815959Sopenharmony_ci AttributeSelector, 59e815959Sopenharmony_ci Traversal, 69e815959Sopenharmony_ci AttributeAction, 79e815959Sopenharmony_ci TraversalType, 89e815959Sopenharmony_ci DataType, 99e815959Sopenharmony_ci} from "./types"; 109e815959Sopenharmony_ci 119e815959Sopenharmony_ciconst reName = /^[^\\#]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\-\u00b0-\uFFFF])+/; 129e815959Sopenharmony_ciconst reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi; 139e815959Sopenharmony_ci 149e815959Sopenharmony_ciconst enum CharCode { 159e815959Sopenharmony_ci LeftParenthesis = 40, 169e815959Sopenharmony_ci RightParenthesis = 41, 179e815959Sopenharmony_ci LeftSquareBracket = 91, 189e815959Sopenharmony_ci RightSquareBracket = 93, 199e815959Sopenharmony_ci Comma = 44, 209e815959Sopenharmony_ci Period = 46, 219e815959Sopenharmony_ci Colon = 58, 229e815959Sopenharmony_ci SingleQuote = 39, 239e815959Sopenharmony_ci DoubleQuote = 34, 249e815959Sopenharmony_ci Plus = 43, 259e815959Sopenharmony_ci Tilde = 126, 269e815959Sopenharmony_ci QuestionMark = 63, 279e815959Sopenharmony_ci ExclamationMark = 33, 289e815959Sopenharmony_ci Slash = 47, 299e815959Sopenharmony_ci Star = 42, 309e815959Sopenharmony_ci Equal = 61, 319e815959Sopenharmony_ci Dollar = 36, 329e815959Sopenharmony_ci Pipe = 124, 339e815959Sopenharmony_ci Circumflex = 94, 349e815959Sopenharmony_ci Asterisk = 42, 359e815959Sopenharmony_ci GreaterThan = 62, 369e815959Sopenharmony_ci LessThan = 60, 379e815959Sopenharmony_ci Hash = 35, 389e815959Sopenharmony_ci LowerI = 105, 399e815959Sopenharmony_ci LowerS = 115, 409e815959Sopenharmony_ci BackSlash = 92, 419e815959Sopenharmony_ci 429e815959Sopenharmony_ci // Whitespace 439e815959Sopenharmony_ci Space = 32, 449e815959Sopenharmony_ci Tab = 9, 459e815959Sopenharmony_ci NewLine = 10, 469e815959Sopenharmony_ci FormFeed = 12, 479e815959Sopenharmony_ci CarriageReturn = 13, 489e815959Sopenharmony_ci} 499e815959Sopenharmony_ci 509e815959Sopenharmony_ciconst actionTypes = new Map<number, AttributeAction>([ 519e815959Sopenharmony_ci [CharCode.Tilde, AttributeAction.Element], 529e815959Sopenharmony_ci [CharCode.Circumflex, AttributeAction.Start], 539e815959Sopenharmony_ci [CharCode.Dollar, AttributeAction.End], 549e815959Sopenharmony_ci [CharCode.Asterisk, AttributeAction.Any], 559e815959Sopenharmony_ci [CharCode.ExclamationMark, AttributeAction.Not], 569e815959Sopenharmony_ci [CharCode.Pipe, AttributeAction.Hyphen], 579e815959Sopenharmony_ci]); 589e815959Sopenharmony_ci 599e815959Sopenharmony_ci// Pseudos, whose data property is parsed as well. 609e815959Sopenharmony_ciconst unpackPseudos = new Set([ 619e815959Sopenharmony_ci "has", 629e815959Sopenharmony_ci "not", 639e815959Sopenharmony_ci "matches", 649e815959Sopenharmony_ci "is", 659e815959Sopenharmony_ci "where", 669e815959Sopenharmony_ci "host", 679e815959Sopenharmony_ci "host-context", 689e815959Sopenharmony_ci]); 699e815959Sopenharmony_ci 709e815959Sopenharmony_ci/** 719e815959Sopenharmony_ci * Checks whether a specific selector is a traversal. 729e815959Sopenharmony_ci * This is useful eg. in swapping the order of elements that 739e815959Sopenharmony_ci * are not traversals. 749e815959Sopenharmony_ci * 759e815959Sopenharmony_ci * @param selector Selector to check. 769e815959Sopenharmony_ci */ 779e815959Sopenharmony_ciexport function isTraversal(selector: Selector): selector is Traversal { 789e815959Sopenharmony_ci switch (selector.type) { 799e815959Sopenharmony_ci case SelectorType.Adjacent: 809e815959Sopenharmony_ci case SelectorType.Child: 819e815959Sopenharmony_ci case SelectorType.Descendant: 829e815959Sopenharmony_ci case SelectorType.Parent: 839e815959Sopenharmony_ci case SelectorType.Sibling: 849e815959Sopenharmony_ci case SelectorType.ColumnCombinator: 859e815959Sopenharmony_ci return true; 869e815959Sopenharmony_ci default: 879e815959Sopenharmony_ci return false; 889e815959Sopenharmony_ci } 899e815959Sopenharmony_ci} 909e815959Sopenharmony_ci 919e815959Sopenharmony_ciconst stripQuotesFromPseudos = new Set(["contains", "icontains"]); 929e815959Sopenharmony_ci 939e815959Sopenharmony_ci// Unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152 949e815959Sopenharmony_cifunction funescape(_: string, escaped: string, escapedWhitespace?: string) { 959e815959Sopenharmony_ci const high = parseInt(escaped, 16) - 0x10000; 969e815959Sopenharmony_ci 979e815959Sopenharmony_ci // NaN means non-codepoint 989e815959Sopenharmony_ci return high !== high || escapedWhitespace 999e815959Sopenharmony_ci ? escaped 1009e815959Sopenharmony_ci : high < 0 1019e815959Sopenharmony_ci ? // BMP codepoint 1029e815959Sopenharmony_ci String.fromCharCode(high + 0x10000) 1039e815959Sopenharmony_ci : // Supplemental Plane codepoint (surrogate pair) 1049e815959Sopenharmony_ci String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00); 1059e815959Sopenharmony_ci} 1069e815959Sopenharmony_ci 1079e815959Sopenharmony_cifunction unescapeCSS(str: string) { 1089e815959Sopenharmony_ci return str.replace(reEscape, funescape); 1099e815959Sopenharmony_ci} 1109e815959Sopenharmony_ci 1119e815959Sopenharmony_cifunction isQuote(c: number): boolean { 1129e815959Sopenharmony_ci return c === CharCode.SingleQuote || c === CharCode.DoubleQuote; 1139e815959Sopenharmony_ci} 1149e815959Sopenharmony_ci 1159e815959Sopenharmony_cifunction isWhitespace(c: number): boolean { 1169e815959Sopenharmony_ci return ( 1179e815959Sopenharmony_ci c === CharCode.Space || 1189e815959Sopenharmony_ci c === CharCode.Tab || 1199e815959Sopenharmony_ci c === CharCode.NewLine || 1209e815959Sopenharmony_ci c === CharCode.FormFeed || 1219e815959Sopenharmony_ci c === CharCode.CarriageReturn 1229e815959Sopenharmony_ci ); 1239e815959Sopenharmony_ci} 1249e815959Sopenharmony_ci 1259e815959Sopenharmony_ci/** 1269e815959Sopenharmony_ci * Parses `selector`, optionally with the passed `options`. 1279e815959Sopenharmony_ci * 1289e815959Sopenharmony_ci * @param selector Selector to parse. 1299e815959Sopenharmony_ci * @param options Options for parsing. 1309e815959Sopenharmony_ci * @returns Returns a two-dimensional array. 1319e815959Sopenharmony_ci * The first dimension represents selectors separated by commas (eg. `sub1, sub2`), 1329e815959Sopenharmony_ci * the second contains the relevant tokens for that selector. 1339e815959Sopenharmony_ci */ 1349e815959Sopenharmony_ciexport function parse(selector: string): Selector[][] { 1359e815959Sopenharmony_ci const subselects: Selector[][] = []; 1369e815959Sopenharmony_ci 1379e815959Sopenharmony_ci const endIndex = parseSelector(subselects, `${selector}`, 0); 1389e815959Sopenharmony_ci 1399e815959Sopenharmony_ci if (endIndex < selector.length) { 1409e815959Sopenharmony_ci throw new Error(`Unmatched selector: ${selector.slice(endIndex)}`); 1419e815959Sopenharmony_ci } 1429e815959Sopenharmony_ci 1439e815959Sopenharmony_ci return subselects; 1449e815959Sopenharmony_ci} 1459e815959Sopenharmony_ci 1469e815959Sopenharmony_cifunction parseSelector( 1479e815959Sopenharmony_ci subselects: Selector[][], 1489e815959Sopenharmony_ci selector: string, 1499e815959Sopenharmony_ci selectorIndex: number 1509e815959Sopenharmony_ci): number { 1519e815959Sopenharmony_ci let tokens: Selector[] = []; 1529e815959Sopenharmony_ci 1539e815959Sopenharmony_ci function getName(offset: number): string { 1549e815959Sopenharmony_ci const match = selector.slice(selectorIndex + offset).match(reName); 1559e815959Sopenharmony_ci 1569e815959Sopenharmony_ci if (!match) { 1579e815959Sopenharmony_ci throw new Error( 1589e815959Sopenharmony_ci `Expected name, found ${selector.slice(selectorIndex)}` 1599e815959Sopenharmony_ci ); 1609e815959Sopenharmony_ci } 1619e815959Sopenharmony_ci 1629e815959Sopenharmony_ci const [name] = match; 1639e815959Sopenharmony_ci selectorIndex += offset + name.length; 1649e815959Sopenharmony_ci return unescapeCSS(name); 1659e815959Sopenharmony_ci } 1669e815959Sopenharmony_ci 1679e815959Sopenharmony_ci function stripWhitespace(offset: number) { 1689e815959Sopenharmony_ci selectorIndex += offset; 1699e815959Sopenharmony_ci 1709e815959Sopenharmony_ci while ( 1719e815959Sopenharmony_ci selectorIndex < selector.length && 1729e815959Sopenharmony_ci isWhitespace(selector.charCodeAt(selectorIndex)) 1739e815959Sopenharmony_ci ) { 1749e815959Sopenharmony_ci selectorIndex++; 1759e815959Sopenharmony_ci } 1769e815959Sopenharmony_ci } 1779e815959Sopenharmony_ci 1789e815959Sopenharmony_ci function readValueWithParenthesis(): string { 1799e815959Sopenharmony_ci selectorIndex += 1; 1809e815959Sopenharmony_ci const start = selectorIndex; 1819e815959Sopenharmony_ci let counter = 1; 1829e815959Sopenharmony_ci 1839e815959Sopenharmony_ci for ( 1849e815959Sopenharmony_ci ; 1859e815959Sopenharmony_ci counter > 0 && selectorIndex < selector.length; 1869e815959Sopenharmony_ci selectorIndex++ 1879e815959Sopenharmony_ci ) { 1889e815959Sopenharmony_ci if ( 1899e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) === 1909e815959Sopenharmony_ci CharCode.LeftParenthesis && 1919e815959Sopenharmony_ci !isEscaped(selectorIndex) 1929e815959Sopenharmony_ci ) { 1939e815959Sopenharmony_ci counter++; 1949e815959Sopenharmony_ci } else if ( 1959e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) === 1969e815959Sopenharmony_ci CharCode.RightParenthesis && 1979e815959Sopenharmony_ci !isEscaped(selectorIndex) 1989e815959Sopenharmony_ci ) { 1999e815959Sopenharmony_ci counter--; 2009e815959Sopenharmony_ci } 2019e815959Sopenharmony_ci } 2029e815959Sopenharmony_ci 2039e815959Sopenharmony_ci if (counter) { 2049e815959Sopenharmony_ci throw new Error("Parenthesis not matched"); 2059e815959Sopenharmony_ci } 2069e815959Sopenharmony_ci 2079e815959Sopenharmony_ci return unescapeCSS(selector.slice(start, selectorIndex - 1)); 2089e815959Sopenharmony_ci } 2099e815959Sopenharmony_ci 2109e815959Sopenharmony_ci function isEscaped(pos: number): boolean { 2119e815959Sopenharmony_ci let slashCount = 0; 2129e815959Sopenharmony_ci 2139e815959Sopenharmony_ci while (selector.charCodeAt(--pos) === CharCode.BackSlash) slashCount++; 2149e815959Sopenharmony_ci return (slashCount & 1) === 1; 2159e815959Sopenharmony_ci } 2169e815959Sopenharmony_ci 2179e815959Sopenharmony_ci function ensureNotTraversal() { 2189e815959Sopenharmony_ci if (tokens.length > 0 && isTraversal(tokens[tokens.length - 1])) { 2199e815959Sopenharmony_ci throw new Error("Did not expect successive traversals."); 2209e815959Sopenharmony_ci } 2219e815959Sopenharmony_ci } 2229e815959Sopenharmony_ci 2239e815959Sopenharmony_ci function addTraversal(type: TraversalType) { 2249e815959Sopenharmony_ci if ( 2259e815959Sopenharmony_ci tokens.length > 0 && 2269e815959Sopenharmony_ci tokens[tokens.length - 1].type === SelectorType.Descendant 2279e815959Sopenharmony_ci ) { 2289e815959Sopenharmony_ci tokens[tokens.length - 1].type = type; 2299e815959Sopenharmony_ci return; 2309e815959Sopenharmony_ci } 2319e815959Sopenharmony_ci 2329e815959Sopenharmony_ci ensureNotTraversal(); 2339e815959Sopenharmony_ci 2349e815959Sopenharmony_ci tokens.push({ type }); 2359e815959Sopenharmony_ci } 2369e815959Sopenharmony_ci 2379e815959Sopenharmony_ci function addSpecialAttribute(name: string, action: AttributeAction) { 2389e815959Sopenharmony_ci tokens.push({ 2399e815959Sopenharmony_ci type: SelectorType.Attribute, 2409e815959Sopenharmony_ci name, 2419e815959Sopenharmony_ci action, 2429e815959Sopenharmony_ci value: getName(1), 2439e815959Sopenharmony_ci namespace: null, 2449e815959Sopenharmony_ci ignoreCase: "quirks", 2459e815959Sopenharmony_ci }); 2469e815959Sopenharmony_ci } 2479e815959Sopenharmony_ci 2489e815959Sopenharmony_ci /** 2499e815959Sopenharmony_ci * We have finished parsing the current part of the selector. 2509e815959Sopenharmony_ci * 2519e815959Sopenharmony_ci * Remove descendant tokens at the end if they exist, 2529e815959Sopenharmony_ci * and return the last index, so that parsing can be 2539e815959Sopenharmony_ci * picked up from here. 2549e815959Sopenharmony_ci */ 2559e815959Sopenharmony_ci function finalizeSubselector() { 2569e815959Sopenharmony_ci if ( 2579e815959Sopenharmony_ci tokens.length && 2589e815959Sopenharmony_ci tokens[tokens.length - 1].type === SelectorType.Descendant 2599e815959Sopenharmony_ci ) { 2609e815959Sopenharmony_ci tokens.pop(); 2619e815959Sopenharmony_ci } 2629e815959Sopenharmony_ci 2639e815959Sopenharmony_ci if (tokens.length === 0) { 2649e815959Sopenharmony_ci throw new Error("Empty sub-selector"); 2659e815959Sopenharmony_ci } 2669e815959Sopenharmony_ci 2679e815959Sopenharmony_ci subselects.push(tokens); 2689e815959Sopenharmony_ci } 2699e815959Sopenharmony_ci 2709e815959Sopenharmony_ci stripWhitespace(0); 2719e815959Sopenharmony_ci 2729e815959Sopenharmony_ci if (selector.length === selectorIndex) { 2739e815959Sopenharmony_ci return selectorIndex; 2749e815959Sopenharmony_ci } 2759e815959Sopenharmony_ci 2769e815959Sopenharmony_ci loop: while (selectorIndex < selector.length) { 2779e815959Sopenharmony_ci const firstChar = selector.charCodeAt(selectorIndex); 2789e815959Sopenharmony_ci 2799e815959Sopenharmony_ci switch (firstChar) { 2809e815959Sopenharmony_ci // Whitespace 2819e815959Sopenharmony_ci case CharCode.Space: 2829e815959Sopenharmony_ci case CharCode.Tab: 2839e815959Sopenharmony_ci case CharCode.NewLine: 2849e815959Sopenharmony_ci case CharCode.FormFeed: 2859e815959Sopenharmony_ci case CharCode.CarriageReturn: { 2869e815959Sopenharmony_ci if ( 2879e815959Sopenharmony_ci tokens.length === 0 || 2889e815959Sopenharmony_ci tokens[0].type !== SelectorType.Descendant 2899e815959Sopenharmony_ci ) { 2909e815959Sopenharmony_ci ensureNotTraversal(); 2919e815959Sopenharmony_ci tokens.push({ type: SelectorType.Descendant }); 2929e815959Sopenharmony_ci } 2939e815959Sopenharmony_ci 2949e815959Sopenharmony_ci stripWhitespace(1); 2959e815959Sopenharmony_ci break; 2969e815959Sopenharmony_ci } 2979e815959Sopenharmony_ci // Traversals 2989e815959Sopenharmony_ci case CharCode.GreaterThan: { 2999e815959Sopenharmony_ci addTraversal(SelectorType.Child); 3009e815959Sopenharmony_ci stripWhitespace(1); 3019e815959Sopenharmony_ci break; 3029e815959Sopenharmony_ci } 3039e815959Sopenharmony_ci case CharCode.LessThan: { 3049e815959Sopenharmony_ci addTraversal(SelectorType.Parent); 3059e815959Sopenharmony_ci stripWhitespace(1); 3069e815959Sopenharmony_ci break; 3079e815959Sopenharmony_ci } 3089e815959Sopenharmony_ci case CharCode.Tilde: { 3099e815959Sopenharmony_ci addTraversal(SelectorType.Sibling); 3109e815959Sopenharmony_ci stripWhitespace(1); 3119e815959Sopenharmony_ci break; 3129e815959Sopenharmony_ci } 3139e815959Sopenharmony_ci case CharCode.Plus: { 3149e815959Sopenharmony_ci addTraversal(SelectorType.Adjacent); 3159e815959Sopenharmony_ci stripWhitespace(1); 3169e815959Sopenharmony_ci break; 3179e815959Sopenharmony_ci } 3189e815959Sopenharmony_ci // Special attribute selectors: .class, #id 3199e815959Sopenharmony_ci case CharCode.Period: { 3209e815959Sopenharmony_ci addSpecialAttribute("class", AttributeAction.Element); 3219e815959Sopenharmony_ci break; 3229e815959Sopenharmony_ci } 3239e815959Sopenharmony_ci case CharCode.Hash: { 3249e815959Sopenharmony_ci addSpecialAttribute("id", AttributeAction.Equals); 3259e815959Sopenharmony_ci break; 3269e815959Sopenharmony_ci } 3279e815959Sopenharmony_ci case CharCode.LeftSquareBracket: { 3289e815959Sopenharmony_ci stripWhitespace(1); 3299e815959Sopenharmony_ci 3309e815959Sopenharmony_ci // Determine attribute name and namespace 3319e815959Sopenharmony_ci 3329e815959Sopenharmony_ci let name: string; 3339e815959Sopenharmony_ci let namespace: string | null = null; 3349e815959Sopenharmony_ci 3359e815959Sopenharmony_ci if (selector.charCodeAt(selectorIndex) === CharCode.Pipe) { 3369e815959Sopenharmony_ci // Equivalent to no namespace 3379e815959Sopenharmony_ci name = getName(1); 3389e815959Sopenharmony_ci } else if (selector.startsWith("*|", selectorIndex)) { 3399e815959Sopenharmony_ci namespace = "*"; 3409e815959Sopenharmony_ci name = getName(2); 3419e815959Sopenharmony_ci } else { 3429e815959Sopenharmony_ci name = getName(0); 3439e815959Sopenharmony_ci 3449e815959Sopenharmony_ci if ( 3459e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) === CharCode.Pipe && 3469e815959Sopenharmony_ci selector.charCodeAt(selectorIndex + 1) !== 3479e815959Sopenharmony_ci CharCode.Equal 3489e815959Sopenharmony_ci ) { 3499e815959Sopenharmony_ci namespace = name; 3509e815959Sopenharmony_ci name = getName(1); 3519e815959Sopenharmony_ci } 3529e815959Sopenharmony_ci } 3539e815959Sopenharmony_ci 3549e815959Sopenharmony_ci stripWhitespace(0); 3559e815959Sopenharmony_ci 3569e815959Sopenharmony_ci // Determine comparison operation 3579e815959Sopenharmony_ci 3589e815959Sopenharmony_ci let action: AttributeAction = AttributeAction.Exists; 3599e815959Sopenharmony_ci const possibleAction = actionTypes.get( 3609e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) 3619e815959Sopenharmony_ci ); 3629e815959Sopenharmony_ci 3639e815959Sopenharmony_ci if (possibleAction) { 3649e815959Sopenharmony_ci action = possibleAction; 3659e815959Sopenharmony_ci 3669e815959Sopenharmony_ci if ( 3679e815959Sopenharmony_ci selector.charCodeAt(selectorIndex + 1) !== 3689e815959Sopenharmony_ci CharCode.Equal 3699e815959Sopenharmony_ci ) { 3709e815959Sopenharmony_ci throw new Error("Expected `=`"); 3719e815959Sopenharmony_ci } 3729e815959Sopenharmony_ci 3739e815959Sopenharmony_ci stripWhitespace(2); 3749e815959Sopenharmony_ci } else if ( 3759e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) === CharCode.Equal 3769e815959Sopenharmony_ci ) { 3779e815959Sopenharmony_ci action = AttributeAction.Equals; 3789e815959Sopenharmony_ci stripWhitespace(1); 3799e815959Sopenharmony_ci } 3809e815959Sopenharmony_ci 3819e815959Sopenharmony_ci // Determine value 3829e815959Sopenharmony_ci 3839e815959Sopenharmony_ci let value = ""; 3849e815959Sopenharmony_ci let ignoreCase: boolean | null = null; 3859e815959Sopenharmony_ci 3869e815959Sopenharmony_ci if (action !== "exists") { 3879e815959Sopenharmony_ci if (isQuote(selector.charCodeAt(selectorIndex))) { 3889e815959Sopenharmony_ci const quote = selector.charCodeAt(selectorIndex); 3899e815959Sopenharmony_ci let sectionEnd = selectorIndex + 1; 3909e815959Sopenharmony_ci while ( 3919e815959Sopenharmony_ci sectionEnd < selector.length && 3929e815959Sopenharmony_ci (selector.charCodeAt(sectionEnd) !== quote || 3939e815959Sopenharmony_ci isEscaped(sectionEnd)) 3949e815959Sopenharmony_ci ) { 3959e815959Sopenharmony_ci sectionEnd += 1; 3969e815959Sopenharmony_ci } 3979e815959Sopenharmony_ci 3989e815959Sopenharmony_ci if (selector.charCodeAt(sectionEnd) !== quote) { 3999e815959Sopenharmony_ci throw new Error("Attribute value didn't end"); 4009e815959Sopenharmony_ci } 4019e815959Sopenharmony_ci 4029e815959Sopenharmony_ci value = unescapeCSS( 4039e815959Sopenharmony_ci selector.slice(selectorIndex + 1, sectionEnd) 4049e815959Sopenharmony_ci ); 4059e815959Sopenharmony_ci selectorIndex = sectionEnd + 1; 4069e815959Sopenharmony_ci } else { 4079e815959Sopenharmony_ci const valueStart = selectorIndex; 4089e815959Sopenharmony_ci 4099e815959Sopenharmony_ci while ( 4109e815959Sopenharmony_ci selectorIndex < selector.length && 4119e815959Sopenharmony_ci ((!isWhitespace( 4129e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) 4139e815959Sopenharmony_ci ) && 4149e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) !== 4159e815959Sopenharmony_ci CharCode.RightSquareBracket) || 4169e815959Sopenharmony_ci isEscaped(selectorIndex)) 4179e815959Sopenharmony_ci ) { 4189e815959Sopenharmony_ci selectorIndex += 1; 4199e815959Sopenharmony_ci } 4209e815959Sopenharmony_ci 4219e815959Sopenharmony_ci value = unescapeCSS( 4229e815959Sopenharmony_ci selector.slice(valueStart, selectorIndex) 4239e815959Sopenharmony_ci ); 4249e815959Sopenharmony_ci } 4259e815959Sopenharmony_ci 4269e815959Sopenharmony_ci stripWhitespace(0); 4279e815959Sopenharmony_ci 4289e815959Sopenharmony_ci // See if we have a force ignore flag 4299e815959Sopenharmony_ci 4309e815959Sopenharmony_ci const forceIgnore = 4319e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) | 0x20; 4329e815959Sopenharmony_ci 4339e815959Sopenharmony_ci // If the forceIgnore flag is set (either `i` or `s`), use that value 4349e815959Sopenharmony_ci if (forceIgnore === CharCode.LowerS) { 4359e815959Sopenharmony_ci ignoreCase = false; 4369e815959Sopenharmony_ci stripWhitespace(1); 4379e815959Sopenharmony_ci } else if (forceIgnore === CharCode.LowerI) { 4389e815959Sopenharmony_ci ignoreCase = true; 4399e815959Sopenharmony_ci stripWhitespace(1); 4409e815959Sopenharmony_ci } 4419e815959Sopenharmony_ci } 4429e815959Sopenharmony_ci 4439e815959Sopenharmony_ci if ( 4449e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) !== 4459e815959Sopenharmony_ci CharCode.RightSquareBracket 4469e815959Sopenharmony_ci ) { 4479e815959Sopenharmony_ci throw new Error("Attribute selector didn't terminate"); 4489e815959Sopenharmony_ci } 4499e815959Sopenharmony_ci 4509e815959Sopenharmony_ci selectorIndex += 1; 4519e815959Sopenharmony_ci 4529e815959Sopenharmony_ci const attributeSelector: AttributeSelector = { 4539e815959Sopenharmony_ci type: SelectorType.Attribute, 4549e815959Sopenharmony_ci name, 4559e815959Sopenharmony_ci action, 4569e815959Sopenharmony_ci value, 4579e815959Sopenharmony_ci namespace, 4589e815959Sopenharmony_ci ignoreCase, 4599e815959Sopenharmony_ci }; 4609e815959Sopenharmony_ci 4619e815959Sopenharmony_ci tokens.push(attributeSelector); 4629e815959Sopenharmony_ci break; 4639e815959Sopenharmony_ci } 4649e815959Sopenharmony_ci case CharCode.Colon: { 4659e815959Sopenharmony_ci if (selector.charCodeAt(selectorIndex + 1) === CharCode.Colon) { 4669e815959Sopenharmony_ci tokens.push({ 4679e815959Sopenharmony_ci type: SelectorType.PseudoElement, 4689e815959Sopenharmony_ci name: getName(2).toLowerCase(), 4699e815959Sopenharmony_ci data: 4709e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) === 4719e815959Sopenharmony_ci CharCode.LeftParenthesis 4729e815959Sopenharmony_ci ? readValueWithParenthesis() 4739e815959Sopenharmony_ci : null, 4749e815959Sopenharmony_ci }); 4759e815959Sopenharmony_ci continue; 4769e815959Sopenharmony_ci } 4779e815959Sopenharmony_ci 4789e815959Sopenharmony_ci const name = getName(1).toLowerCase(); 4799e815959Sopenharmony_ci let data: DataType = null; 4809e815959Sopenharmony_ci 4819e815959Sopenharmony_ci if ( 4829e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) === 4839e815959Sopenharmony_ci CharCode.LeftParenthesis 4849e815959Sopenharmony_ci ) { 4859e815959Sopenharmony_ci if (unpackPseudos.has(name)) { 4869e815959Sopenharmony_ci if (isQuote(selector.charCodeAt(selectorIndex + 1))) { 4879e815959Sopenharmony_ci throw new Error( 4889e815959Sopenharmony_ci `Pseudo-selector ${name} cannot be quoted` 4899e815959Sopenharmony_ci ); 4909e815959Sopenharmony_ci } 4919e815959Sopenharmony_ci 4929e815959Sopenharmony_ci data = []; 4939e815959Sopenharmony_ci selectorIndex = parseSelector( 4949e815959Sopenharmony_ci data, 4959e815959Sopenharmony_ci selector, 4969e815959Sopenharmony_ci selectorIndex + 1 4979e815959Sopenharmony_ci ); 4989e815959Sopenharmony_ci 4999e815959Sopenharmony_ci if ( 5009e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) !== 5019e815959Sopenharmony_ci CharCode.RightParenthesis 5029e815959Sopenharmony_ci ) { 5039e815959Sopenharmony_ci throw new Error( 5049e815959Sopenharmony_ci `Missing closing parenthesis in :${name} (${selector})` 5059e815959Sopenharmony_ci ); 5069e815959Sopenharmony_ci } 5079e815959Sopenharmony_ci 5089e815959Sopenharmony_ci selectorIndex += 1; 5099e815959Sopenharmony_ci } else { 5109e815959Sopenharmony_ci data = readValueWithParenthesis(); 5119e815959Sopenharmony_ci 5129e815959Sopenharmony_ci if (stripQuotesFromPseudos.has(name)) { 5139e815959Sopenharmony_ci const quot = data.charCodeAt(0); 5149e815959Sopenharmony_ci 5159e815959Sopenharmony_ci if ( 5169e815959Sopenharmony_ci quot === data.charCodeAt(data.length - 1) && 5179e815959Sopenharmony_ci isQuote(quot) 5189e815959Sopenharmony_ci ) { 5199e815959Sopenharmony_ci data = data.slice(1, -1); 5209e815959Sopenharmony_ci } 5219e815959Sopenharmony_ci } 5229e815959Sopenharmony_ci 5239e815959Sopenharmony_ci data = unescapeCSS(data); 5249e815959Sopenharmony_ci } 5259e815959Sopenharmony_ci } 5269e815959Sopenharmony_ci 5279e815959Sopenharmony_ci tokens.push({ type: SelectorType.Pseudo, name, data }); 5289e815959Sopenharmony_ci break; 5299e815959Sopenharmony_ci } 5309e815959Sopenharmony_ci case CharCode.Comma: { 5319e815959Sopenharmony_ci finalizeSubselector(); 5329e815959Sopenharmony_ci tokens = []; 5339e815959Sopenharmony_ci stripWhitespace(1); 5349e815959Sopenharmony_ci break; 5359e815959Sopenharmony_ci } 5369e815959Sopenharmony_ci default: { 5379e815959Sopenharmony_ci if (selector.startsWith("/*", selectorIndex)) { 5389e815959Sopenharmony_ci const endIndex = selector.indexOf("*/", selectorIndex + 2); 5399e815959Sopenharmony_ci 5409e815959Sopenharmony_ci if (endIndex < 0) { 5419e815959Sopenharmony_ci throw new Error("Comment was not terminated"); 5429e815959Sopenharmony_ci } 5439e815959Sopenharmony_ci 5449e815959Sopenharmony_ci selectorIndex = endIndex + 2; 5459e815959Sopenharmony_ci 5469e815959Sopenharmony_ci // Remove leading whitespace 5479e815959Sopenharmony_ci if (tokens.length === 0) { 5489e815959Sopenharmony_ci stripWhitespace(0); 5499e815959Sopenharmony_ci } 5509e815959Sopenharmony_ci 5519e815959Sopenharmony_ci break; 5529e815959Sopenharmony_ci } 5539e815959Sopenharmony_ci 5549e815959Sopenharmony_ci let namespace = null; 5559e815959Sopenharmony_ci let name: string; 5569e815959Sopenharmony_ci 5579e815959Sopenharmony_ci if (firstChar === CharCode.Asterisk) { 5589e815959Sopenharmony_ci selectorIndex += 1; 5599e815959Sopenharmony_ci name = "*"; 5609e815959Sopenharmony_ci } else if (firstChar === CharCode.Pipe) { 5619e815959Sopenharmony_ci name = ""; 5629e815959Sopenharmony_ci 5639e815959Sopenharmony_ci if ( 5649e815959Sopenharmony_ci selector.charCodeAt(selectorIndex + 1) === CharCode.Pipe 5659e815959Sopenharmony_ci ) { 5669e815959Sopenharmony_ci addTraversal(SelectorType.ColumnCombinator); 5679e815959Sopenharmony_ci stripWhitespace(2); 5689e815959Sopenharmony_ci break; 5699e815959Sopenharmony_ci } 5709e815959Sopenharmony_ci } else if (reName.test(selector.slice(selectorIndex))) { 5719e815959Sopenharmony_ci name = getName(0); 5729e815959Sopenharmony_ci } else { 5739e815959Sopenharmony_ci break loop; 5749e815959Sopenharmony_ci } 5759e815959Sopenharmony_ci 5769e815959Sopenharmony_ci if ( 5779e815959Sopenharmony_ci selector.charCodeAt(selectorIndex) === CharCode.Pipe && 5789e815959Sopenharmony_ci selector.charCodeAt(selectorIndex + 1) !== CharCode.Pipe 5799e815959Sopenharmony_ci ) { 5809e815959Sopenharmony_ci namespace = name; 5819e815959Sopenharmony_ci if ( 5829e815959Sopenharmony_ci selector.charCodeAt(selectorIndex + 1) === 5839e815959Sopenharmony_ci CharCode.Asterisk 5849e815959Sopenharmony_ci ) { 5859e815959Sopenharmony_ci name = "*"; 5869e815959Sopenharmony_ci selectorIndex += 2; 5879e815959Sopenharmony_ci } else { 5889e815959Sopenharmony_ci name = getName(1); 5899e815959Sopenharmony_ci } 5909e815959Sopenharmony_ci } 5919e815959Sopenharmony_ci 5929e815959Sopenharmony_ci tokens.push( 5939e815959Sopenharmony_ci name === "*" 5949e815959Sopenharmony_ci ? { type: SelectorType.Universal, namespace } 5959e815959Sopenharmony_ci : { type: SelectorType.Tag, name, namespace } 5969e815959Sopenharmony_ci ); 5979e815959Sopenharmony_ci } 5989e815959Sopenharmony_ci } 5999e815959Sopenharmony_ci } 6009e815959Sopenharmony_ci 6019e815959Sopenharmony_ci finalizeSubselector(); 6029e815959Sopenharmony_ci return selectorIndex; 6039e815959Sopenharmony_ci} 604