19e815959Sopenharmony_ciimport { Selector, SelectorType, AttributeAction } from "./types"; 29e815959Sopenharmony_ci 39e815959Sopenharmony_ciconst attribValChars = ["\\", '"']; 49e815959Sopenharmony_ciconst pseudoValChars = [...attribValChars, "(", ")"]; 59e815959Sopenharmony_ci 69e815959Sopenharmony_ciconst charsToEscapeInAttributeValue = new Set( 79e815959Sopenharmony_ci attribValChars.map((c) => c.charCodeAt(0)) 89e815959Sopenharmony_ci); 99e815959Sopenharmony_ciconst charsToEscapeInPseudoValue = new Set( 109e815959Sopenharmony_ci pseudoValChars.map((c) => c.charCodeAt(0)) 119e815959Sopenharmony_ci); 129e815959Sopenharmony_ciconst charsToEscapeInName = new Set( 139e815959Sopenharmony_ci [ 149e815959Sopenharmony_ci ...pseudoValChars, 159e815959Sopenharmony_ci "~", 169e815959Sopenharmony_ci "^", 179e815959Sopenharmony_ci "$", 189e815959Sopenharmony_ci "*", 199e815959Sopenharmony_ci "+", 209e815959Sopenharmony_ci "!", 219e815959Sopenharmony_ci "|", 229e815959Sopenharmony_ci ":", 239e815959Sopenharmony_ci "[", 249e815959Sopenharmony_ci "]", 259e815959Sopenharmony_ci " ", 269e815959Sopenharmony_ci ".", 279e815959Sopenharmony_ci ].map((c) => c.charCodeAt(0)) 289e815959Sopenharmony_ci); 299e815959Sopenharmony_ci 309e815959Sopenharmony_ci/** 319e815959Sopenharmony_ci * Turns `selector` back into a string. 329e815959Sopenharmony_ci * 339e815959Sopenharmony_ci * @param selector Selector to stringify. 349e815959Sopenharmony_ci */ 359e815959Sopenharmony_ciexport function stringify(selector: Selector[][]): string { 369e815959Sopenharmony_ci return selector 379e815959Sopenharmony_ci .map((token) => token.map(stringifyToken).join("")) 389e815959Sopenharmony_ci .join(", "); 399e815959Sopenharmony_ci} 409e815959Sopenharmony_ci 419e815959Sopenharmony_cifunction stringifyToken( 429e815959Sopenharmony_ci token: Selector, 439e815959Sopenharmony_ci index: number, 449e815959Sopenharmony_ci arr: Selector[] 459e815959Sopenharmony_ci): string { 469e815959Sopenharmony_ci switch (token.type) { 479e815959Sopenharmony_ci // Simple types 489e815959Sopenharmony_ci case SelectorType.Child: 499e815959Sopenharmony_ci return index === 0 ? "> " : " > "; 509e815959Sopenharmony_ci case SelectorType.Parent: 519e815959Sopenharmony_ci return index === 0 ? "< " : " < "; 529e815959Sopenharmony_ci case SelectorType.Sibling: 539e815959Sopenharmony_ci return index === 0 ? "~ " : " ~ "; 549e815959Sopenharmony_ci case SelectorType.Adjacent: 559e815959Sopenharmony_ci return index === 0 ? "+ " : " + "; 569e815959Sopenharmony_ci case SelectorType.Descendant: 579e815959Sopenharmony_ci return " "; 589e815959Sopenharmony_ci case SelectorType.ColumnCombinator: 599e815959Sopenharmony_ci return index === 0 ? "|| " : " || "; 609e815959Sopenharmony_ci case SelectorType.Universal: 619e815959Sopenharmony_ci // Return an empty string if the selector isn't needed. 629e815959Sopenharmony_ci return token.namespace === "*" && 639e815959Sopenharmony_ci index + 1 < arr.length && 649e815959Sopenharmony_ci "name" in arr[index + 1] 659e815959Sopenharmony_ci ? "" 669e815959Sopenharmony_ci : `${getNamespace(token.namespace)}*`; 679e815959Sopenharmony_ci 689e815959Sopenharmony_ci case SelectorType.Tag: 699e815959Sopenharmony_ci return getNamespacedName(token); 709e815959Sopenharmony_ci 719e815959Sopenharmony_ci case SelectorType.PseudoElement: 729e815959Sopenharmony_ci return `::${escapeName(token.name, charsToEscapeInName)}${ 739e815959Sopenharmony_ci token.data === null 749e815959Sopenharmony_ci ? "" 759e815959Sopenharmony_ci : `(${escapeName(token.data, charsToEscapeInPseudoValue)})` 769e815959Sopenharmony_ci }`; 779e815959Sopenharmony_ci 789e815959Sopenharmony_ci case SelectorType.Pseudo: 799e815959Sopenharmony_ci return `:${escapeName(token.name, charsToEscapeInName)}${ 809e815959Sopenharmony_ci token.data === null 819e815959Sopenharmony_ci ? "" 829e815959Sopenharmony_ci : `(${ 839e815959Sopenharmony_ci typeof token.data === "string" 849e815959Sopenharmony_ci ? escapeName( 859e815959Sopenharmony_ci token.data, 869e815959Sopenharmony_ci charsToEscapeInPseudoValue 879e815959Sopenharmony_ci ) 889e815959Sopenharmony_ci : stringify(token.data) 899e815959Sopenharmony_ci })` 909e815959Sopenharmony_ci }`; 919e815959Sopenharmony_ci 929e815959Sopenharmony_ci case SelectorType.Attribute: { 939e815959Sopenharmony_ci if ( 949e815959Sopenharmony_ci token.name === "id" && 959e815959Sopenharmony_ci token.action === AttributeAction.Equals && 969e815959Sopenharmony_ci token.ignoreCase === "quirks" && 979e815959Sopenharmony_ci !token.namespace 989e815959Sopenharmony_ci ) { 999e815959Sopenharmony_ci return `#${escapeName(token.value, charsToEscapeInName)}`; 1009e815959Sopenharmony_ci } 1019e815959Sopenharmony_ci if ( 1029e815959Sopenharmony_ci token.name === "class" && 1039e815959Sopenharmony_ci token.action === AttributeAction.Element && 1049e815959Sopenharmony_ci token.ignoreCase === "quirks" && 1059e815959Sopenharmony_ci !token.namespace 1069e815959Sopenharmony_ci ) { 1079e815959Sopenharmony_ci return `.${escapeName(token.value, charsToEscapeInName)}`; 1089e815959Sopenharmony_ci } 1099e815959Sopenharmony_ci 1109e815959Sopenharmony_ci const name = getNamespacedName(token); 1119e815959Sopenharmony_ci 1129e815959Sopenharmony_ci if (token.action === AttributeAction.Exists) { 1139e815959Sopenharmony_ci return `[${name}]`; 1149e815959Sopenharmony_ci } 1159e815959Sopenharmony_ci 1169e815959Sopenharmony_ci return `[${name}${getActionValue(token.action)}="${escapeName( 1179e815959Sopenharmony_ci token.value, 1189e815959Sopenharmony_ci charsToEscapeInAttributeValue 1199e815959Sopenharmony_ci )}"${ 1209e815959Sopenharmony_ci token.ignoreCase === null ? "" : token.ignoreCase ? " i" : " s" 1219e815959Sopenharmony_ci }]`; 1229e815959Sopenharmony_ci } 1239e815959Sopenharmony_ci } 1249e815959Sopenharmony_ci} 1259e815959Sopenharmony_ci 1269e815959Sopenharmony_cifunction getActionValue(action: AttributeAction): string { 1279e815959Sopenharmony_ci switch (action) { 1289e815959Sopenharmony_ci case AttributeAction.Equals: 1299e815959Sopenharmony_ci return ""; 1309e815959Sopenharmony_ci case AttributeAction.Element: 1319e815959Sopenharmony_ci return "~"; 1329e815959Sopenharmony_ci case AttributeAction.Start: 1339e815959Sopenharmony_ci return "^"; 1349e815959Sopenharmony_ci case AttributeAction.End: 1359e815959Sopenharmony_ci return "$"; 1369e815959Sopenharmony_ci case AttributeAction.Any: 1379e815959Sopenharmony_ci return "*"; 1389e815959Sopenharmony_ci case AttributeAction.Not: 1399e815959Sopenharmony_ci return "!"; 1409e815959Sopenharmony_ci case AttributeAction.Hyphen: 1419e815959Sopenharmony_ci return "|"; 1429e815959Sopenharmony_ci case AttributeAction.Exists: 1439e815959Sopenharmony_ci throw new Error("Shouldn't be here"); 1449e815959Sopenharmony_ci } 1459e815959Sopenharmony_ci} 1469e815959Sopenharmony_ci 1479e815959Sopenharmony_cifunction getNamespacedName(token: { 1489e815959Sopenharmony_ci name: string; 1499e815959Sopenharmony_ci namespace: string | null; 1509e815959Sopenharmony_ci}): string { 1519e815959Sopenharmony_ci return `${getNamespace(token.namespace)}${escapeName( 1529e815959Sopenharmony_ci token.name, 1539e815959Sopenharmony_ci charsToEscapeInName 1549e815959Sopenharmony_ci )}`; 1559e815959Sopenharmony_ci} 1569e815959Sopenharmony_ci 1579e815959Sopenharmony_cifunction getNamespace(namespace: string | null): string { 1589e815959Sopenharmony_ci return namespace !== null 1599e815959Sopenharmony_ci ? `${ 1609e815959Sopenharmony_ci namespace === "*" 1619e815959Sopenharmony_ci ? "*" 1629e815959Sopenharmony_ci : escapeName(namespace, charsToEscapeInName) 1639e815959Sopenharmony_ci }|` 1649e815959Sopenharmony_ci : ""; 1659e815959Sopenharmony_ci} 1669e815959Sopenharmony_ci 1679e815959Sopenharmony_cifunction escapeName(str: string, charsToEscape: Set<number>): string { 1689e815959Sopenharmony_ci let lastIdx = 0; 1699e815959Sopenharmony_ci let ret = ""; 1709e815959Sopenharmony_ci 1719e815959Sopenharmony_ci for (let i = 0; i < str.length; i++) { 1729e815959Sopenharmony_ci if (charsToEscape.has(str.charCodeAt(i))) { 1739e815959Sopenharmony_ci ret += `${str.slice(lastIdx, i)}\\${str.charAt(i)}`; 1749e815959Sopenharmony_ci lastIdx = i + 1; 1759e815959Sopenharmony_ci } 1769e815959Sopenharmony_ci } 1779e815959Sopenharmony_ci 1789e815959Sopenharmony_ci return ret.length > 0 ? ret + str.slice(lastIdx) : str; 1799e815959Sopenharmony_ci} 180