blob: 9f8144cf928ad69d6061b6ada429c8f9c0c529fd [file] [log] [blame]
Tim van der Lippe706ec962021-06-04 13:24:42 +01001"use strict";
2var __spreadArrays = (this && this.__spreadArrays) || function () {
3 for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
4 for (var r = Array(s), k = 0, i = 0; i < il; i++)
5 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
6 r[k] = a[j];
7 return r;
8};
9Object.defineProperty(exports, "__esModule", { value: true });
10exports.isTraversal = void 0;
11var reName = /^[^\\#]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\-\u00b0-\uFFFF])+/;
12var reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi;
13// Modified version of https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L87
14var reAttr = /^\s*(?:(\*|[-\w]*)\|)?((?:\\.|[\w\u00b0-\uFFFF-])+)\s*(?:(\S?)=\s*(?:(['"])((?:[^\\]|\\[^])*?)\4|(#?(?:\\.|[\w\u00b0-\uFFFF-])*)|)|)\s*([iI])?\]/;
15var actionTypes = {
16 undefined: "exists",
17 "": "equals",
18 "~": "element",
19 "^": "start",
20 $: "end",
21 "*": "any",
22 "!": "not",
23 "|": "hyphen",
24};
25var Traversals = {
26 ">": "child",
27 "<": "parent",
28 "~": "sibling",
29 "+": "adjacent",
30};
31var attribSelectors = {
32 "#": ["id", "equals"],
33 ".": ["class", "element"],
34};
35// Pseudos, whose data property is parsed as well.
36var unpackPseudos = new Set([
37 "has",
38 "not",
39 "matches",
40 "is",
41 "host",
42 "host-context",
43]);
44var traversalNames = new Set(__spreadArrays([
45 "descendant"
46], Object.keys(Traversals).map(function (k) { return Traversals[k]; })));
47/**
48 * Checks whether a specific selector is a traversal.
49 * This is useful eg. in swapping the order of elements that
50 * are not traversals.
51 *
52 * @param selector Selector to check.
53 */
54function isTraversal(selector) {
55 return traversalNames.has(selector.type);
56}
57exports.isTraversal = isTraversal;
58var stripQuotesFromPseudos = new Set(["contains", "icontains"]);
59var quotes = new Set(['"', "'"]);
60// Unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152
61function funescape(_, escaped, escapedWhitespace) {
62 var high = parseInt(escaped, 16) - 0x10000;
63 // NaN means non-codepoint
64 return high !== high || escapedWhitespace
65 ? escaped
66 : high < 0
67 ? // BMP codepoint
68 String.fromCharCode(high + 0x10000)
69 : // Supplemental Plane codepoint (surrogate pair)
70 String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
71}
72function unescapeCSS(str) {
73 return str.replace(reEscape, funescape);
74}
75function isWhitespace(c) {
76 return c === " " || c === "\n" || c === "\t" || c === "\f" || c === "\r";
77}
78/**
79 * Parses `selector`, optionally with the passed `options`.
80 *
81 * @param selector Selector to parse.
82 * @param options Options for parsing.
83 * @returns Returns a two-dimensional array.
84 * The first dimension represents selectors separated by commas (eg. `sub1, sub2`),
85 * the second contains the relevant tokens for that selector.
86 */
87function parse(selector, options) {
88 var subselects = [];
89 var endIndex = parseSelector(subselects, "" + selector, options, 0);
90 if (endIndex < selector.length) {
91 throw new Error("Unmatched selector: " + selector.slice(endIndex));
92 }
93 return subselects;
94}
95exports.default = parse;
96function parseSelector(subselects, selector, options, selectorIndex) {
97 var _a, _b;
98 if (options === void 0) { options = {}; }
99 var tokens = [];
100 var sawWS = false;
101 function getName(offset) {
102 var match = selector.slice(selectorIndex + offset).match(reName);
103 if (!match) {
104 throw new Error("Expected name, found " + selector.slice(selectorIndex));
105 }
106 var name = match[0];
107 selectorIndex += offset + name.length;
108 return unescapeCSS(name);
109 }
110 function stripWhitespace(offset) {
111 while (isWhitespace(selector.charAt(selectorIndex + offset)))
112 offset++;
113 selectorIndex += offset;
114 }
115 function isEscaped(pos) {
116 var slashCount = 0;
117 while (selector.charAt(--pos) === "\\")
118 slashCount++;
119 return (slashCount & 1) === 1;
120 }
121 function ensureNotTraversal() {
122 if (tokens.length > 0 && isTraversal(tokens[tokens.length - 1])) {
123 throw new Error("Did not expect successive traversals.");
124 }
125 }
126 stripWhitespace(0);
127 while (selector !== "") {
128 var firstChar = selector.charAt(selectorIndex);
129 if (isWhitespace(firstChar)) {
130 sawWS = true;
131 stripWhitespace(1);
132 }
133 else if (firstChar in Traversals) {
134 ensureNotTraversal();
135 tokens.push({ type: Traversals[firstChar] });
136 sawWS = false;
137 stripWhitespace(1);
138 }
139 else if (firstChar === ",") {
140 if (tokens.length === 0) {
141 throw new Error("Empty sub-selector");
142 }
143 subselects.push(tokens);
144 tokens = [];
145 sawWS = false;
146 stripWhitespace(1);
147 }
148 else {
149 if (sawWS) {
150 ensureNotTraversal();
151 tokens.push({ type: "descendant" });
152 sawWS = false;
153 }
154 if (firstChar in attribSelectors) {
155 var _c = attribSelectors[firstChar], name_1 = _c[0], action = _c[1];
156 tokens.push({
157 type: "attribute",
158 name: name_1,
159 action: action,
160 value: getName(1),
161 ignoreCase: false,
162 namespace: null,
163 });
164 }
165 else if (firstChar === "[") {
166 var attributeMatch = selector
167 .slice(selectorIndex + 1)
168 .match(reAttr);
169 if (!attributeMatch) {
170 throw new Error("Malformed attribute selector: " + selector.slice(selectorIndex));
171 }
172 var completeSelector = attributeMatch[0], _d = attributeMatch[1], namespace = _d === void 0 ? null : _d, baseName = attributeMatch[2], actionType = attributeMatch[3], _e = attributeMatch[5], quotedValue = _e === void 0 ? "" : _e, _f = attributeMatch[6], value = _f === void 0 ? quotedValue : _f, ignoreCase = attributeMatch[7];
173 selectorIndex += completeSelector.length + 1;
174 var name_2 = unescapeCSS(baseName);
175 if ((_a = options.lowerCaseAttributeNames) !== null && _a !== void 0 ? _a : !options.xmlMode) {
176 name_2 = name_2.toLowerCase();
177 }
178 tokens.push({
179 type: "attribute",
180 name: name_2,
181 action: actionTypes[actionType],
182 value: unescapeCSS(value),
183 namespace: namespace,
184 ignoreCase: !!ignoreCase,
185 });
186 }
187 else if (firstChar === ":") {
188 if (selector.charAt(selectorIndex + 1) === ":") {
189 tokens.push({
190 type: "pseudo-element",
191 name: getName(2).toLowerCase(),
192 });
193 continue;
194 }
195 var name_3 = getName(1).toLowerCase();
196 var data = null;
197 if (selector.charAt(selectorIndex) === "(") {
198 if (unpackPseudos.has(name_3)) {
199 if (quotes.has(selector.charAt(selectorIndex + 1))) {
200 throw new Error("Pseudo-selector " + name_3 + " cannot be quoted");
201 }
202 data = [];
203 selectorIndex = parseSelector(data, selector, options, selectorIndex + 1);
204 if (selector.charAt(selectorIndex) !== ")") {
205 throw new Error("Missing closing parenthesis in :" + name_3 + " (" + selector + ")");
206 }
207 selectorIndex += 1;
208 }
209 else {
210 selectorIndex += 1;
211 var start = selectorIndex;
212 var counter = 1;
213 for (; counter > 0 && selectorIndex < selector.length; selectorIndex++) {
214 if (selector.charAt(selectorIndex) === "(" &&
215 !isEscaped(selectorIndex)) {
216 counter++;
217 }
218 else if (selector.charAt(selectorIndex) === ")" &&
219 !isEscaped(selectorIndex)) {
220 counter--;
221 }
222 }
223 if (counter) {
224 throw new Error("Parenthesis not matched");
225 }
226 data = selector.slice(start, selectorIndex - 1);
227 if (stripQuotesFromPseudos.has(name_3)) {
228 var quot = data.charAt(0);
229 if (quot === data.slice(-1) && quotes.has(quot)) {
230 data = data.slice(1, -1);
231 }
232 data = unescapeCSS(data);
233 }
234 }
235 }
236 tokens.push({ type: "pseudo", name: name_3, data: data });
237 }
238 else {
239 var namespace = null;
240 var name_4 = void 0;
241 if (firstChar === "*") {
242 selectorIndex += 1;
243 name_4 = "*";
244 }
245 else if (reName.test(selector.slice(selectorIndex))) {
246 name_4 = getName(0);
247 }
248 else {
249 /*
250 * We have finished parsing the selector.
251 * Remove descendant tokens at the end if they exist,
252 * and return the last index, so that parsing can be
253 * picked up from here.
254 */
255 if (tokens.length &&
256 tokens[tokens.length - 1].type === "descendant") {
257 tokens.pop();
258 }
259 addToken(subselects, tokens);
260 return selectorIndex;
261 }
262 if (selector.charAt(selectorIndex) === "|") {
263 namespace = name_4;
264 if (selector.charAt(selectorIndex + 1) === "*") {
265 name_4 = "*";
266 selectorIndex += 2;
267 }
268 else {
269 name_4 = getName(1);
270 }
271 }
272 if (name_4 === "*") {
273 tokens.push({ type: "universal", namespace: namespace });
274 }
275 else {
276 if ((_b = options.lowerCaseTags) !== null && _b !== void 0 ? _b : !options.xmlMode) {
277 name_4 = name_4.toLowerCase();
278 }
279 tokens.push({ type: "tag", name: name_4, namespace: namespace });
280 }
281 }
282 }
283 }
284 addToken(subselects, tokens);
285 return selectorIndex;
286}
287function addToken(subselects, tokens) {
288 if (subselects.length > 0 && tokens.length === 0) {
289 throw new Error("Empty sub-selector");
290 }
291 subselects.push(tokens);
292}