123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- "use strict";
- var __spreadArray = (this && this.__spreadArray) || function (to, from) {
- for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
- to[j] = from[i];
- return to;
- };
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.isTraversal = void 0;
- var reName = /^[^\\#]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\-\u00b0-\uFFFF])+/;
- var reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi;
- var actionTypes = new Map([
- ["~", "element"],
- ["^", "start"],
- ["$", "end"],
- ["*", "any"],
- ["!", "not"],
- ["|", "hyphen"],
- ]);
- var Traversals = {
- ">": "child",
- "<": "parent",
- "~": "sibling",
- "+": "adjacent",
- };
- var attribSelectors = {
- "#": ["id", "equals"],
- ".": ["class", "element"],
- };
- // Pseudos, whose data property is parsed as well.
- var unpackPseudos = new Set([
- "has",
- "not",
- "matches",
- "is",
- "host",
- "host-context",
- ]);
- var traversalNames = new Set(__spreadArray([
- "descendant"
- ], Object.keys(Traversals).map(function (k) { return Traversals[k]; })));
- /**
- * Attributes that are case-insensitive in HTML.
- *
- * @private
- * @see https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors
- */
- var caseInsensitiveAttributes = new Set([
- "accept",
- "accept-charset",
- "align",
- "alink",
- "axis",
- "bgcolor",
- "charset",
- "checked",
- "clear",
- "codetype",
- "color",
- "compact",
- "declare",
- "defer",
- "dir",
- "direction",
- "disabled",
- "enctype",
- "face",
- "frame",
- "hreflang",
- "http-equiv",
- "lang",
- "language",
- "link",
- "media",
- "method",
- "multiple",
- "nohref",
- "noresize",
- "noshade",
- "nowrap",
- "readonly",
- "rel",
- "rev",
- "rules",
- "scope",
- "scrolling",
- "selected",
- "shape",
- "target",
- "text",
- "type",
- "valign",
- "valuetype",
- "vlink",
- ]);
- /**
- * Checks whether a specific selector is a traversal.
- * This is useful eg. in swapping the order of elements that
- * are not traversals.
- *
- * @param selector Selector to check.
- */
- function isTraversal(selector) {
- return traversalNames.has(selector.type);
- }
- exports.isTraversal = isTraversal;
- var stripQuotesFromPseudos = new Set(["contains", "icontains"]);
- var quotes = new Set(['"', "'"]);
- // Unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152
- function funescape(_, escaped, escapedWhitespace) {
- var high = parseInt(escaped, 16) - 0x10000;
- // NaN means non-codepoint
- return high !== high || escapedWhitespace
- ? escaped
- : high < 0
- ? // BMP codepoint
- String.fromCharCode(high + 0x10000)
- : // Supplemental Plane codepoint (surrogate pair)
- String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
- }
- function unescapeCSS(str) {
- return str.replace(reEscape, funescape);
- }
- function isWhitespace(c) {
- return c === " " || c === "\n" || c === "\t" || c === "\f" || c === "\r";
- }
- /**
- * Parses `selector`, optionally with the passed `options`.
- *
- * @param selector Selector to parse.
- * @param options Options for parsing.
- * @returns Returns a two-dimensional array.
- * The first dimension represents selectors separated by commas (eg. `sub1, sub2`),
- * the second contains the relevant tokens for that selector.
- */
- function parse(selector, options) {
- var subselects = [];
- var endIndex = parseSelector(subselects, "" + selector, options, 0);
- if (endIndex < selector.length) {
- throw new Error("Unmatched selector: " + selector.slice(endIndex));
- }
- return subselects;
- }
- exports.default = parse;
- function parseSelector(subselects, selector, options, selectorIndex) {
- var _a, _b;
- if (options === void 0) { options = {}; }
- var tokens = [];
- var sawWS = false;
- function getName(offset) {
- var match = selector.slice(selectorIndex + offset).match(reName);
- if (!match) {
- throw new Error("Expected name, found " + selector.slice(selectorIndex));
- }
- var name = match[0];
- selectorIndex += offset + name.length;
- return unescapeCSS(name);
- }
- function stripWhitespace(offset) {
- while (isWhitespace(selector.charAt(selectorIndex + offset)))
- offset++;
- selectorIndex += offset;
- }
- function isEscaped(pos) {
- var slashCount = 0;
- while (selector.charAt(--pos) === "\\")
- slashCount++;
- return (slashCount & 1) === 1;
- }
- function ensureNotTraversal() {
- if (tokens.length > 0 && isTraversal(tokens[tokens.length - 1])) {
- throw new Error("Did not expect successive traversals.");
- }
- }
- stripWhitespace(0);
- while (selector !== "") {
- var firstChar = selector.charAt(selectorIndex);
- if (isWhitespace(firstChar)) {
- sawWS = true;
- stripWhitespace(1);
- }
- else if (firstChar in Traversals) {
- ensureNotTraversal();
- tokens.push({ type: Traversals[firstChar] });
- sawWS = false;
- stripWhitespace(1);
- }
- else if (firstChar === ",") {
- if (tokens.length === 0) {
- throw new Error("Empty sub-selector");
- }
- subselects.push(tokens);
- tokens = [];
- sawWS = false;
- stripWhitespace(1);
- }
- else if (selector.startsWith("/*", selectorIndex)) {
- var endIndex = selector.indexOf("*/", selectorIndex + 2);
- if (endIndex < 0) {
- throw new Error("Comment was not terminated");
- }
- selectorIndex = endIndex + 2;
- }
- else {
- if (sawWS) {
- ensureNotTraversal();
- tokens.push({ type: "descendant" });
- sawWS = false;
- }
- if (firstChar in attribSelectors) {
- var _c = attribSelectors[firstChar], name_1 = _c[0], action = _c[1];
- tokens.push({
- type: "attribute",
- name: name_1,
- action: action,
- value: getName(1),
- namespace: null,
- // TODO: Add quirksMode option, which makes `ignoreCase` `true` for HTML.
- ignoreCase: options.xmlMode ? null : false,
- });
- }
- else if (firstChar === "[") {
- stripWhitespace(1);
- // Determine attribute name and namespace
- var name_2 = void 0;
- var namespace = null;
- if (selector.charAt(selectorIndex) === "|") {
- namespace = "";
- selectorIndex += 1;
- }
- if (selector.startsWith("*|", selectorIndex)) {
- namespace = "*";
- selectorIndex += 2;
- }
- name_2 = getName(0);
- if (namespace === null &&
- selector.charAt(selectorIndex) === "|" &&
- selector.charAt(selectorIndex + 1) !== "=") {
- namespace = name_2;
- name_2 = getName(1);
- }
- if ((_a = options.lowerCaseAttributeNames) !== null && _a !== void 0 ? _a : !options.xmlMode) {
- name_2 = name_2.toLowerCase();
- }
- stripWhitespace(0);
- // Determine comparison operation
- var action = "exists";
- var possibleAction = actionTypes.get(selector.charAt(selectorIndex));
- if (possibleAction) {
- action = possibleAction;
- if (selector.charAt(selectorIndex + 1) !== "=") {
- throw new Error("Expected `=`");
- }
- stripWhitespace(2);
- }
- else if (selector.charAt(selectorIndex) === "=") {
- action = "equals";
- stripWhitespace(1);
- }
- // Determine value
- var value = "";
- var ignoreCase = null;
- if (action !== "exists") {
- if (quotes.has(selector.charAt(selectorIndex))) {
- var quote = selector.charAt(selectorIndex);
- var sectionEnd = selectorIndex + 1;
- while (sectionEnd < selector.length &&
- (selector.charAt(sectionEnd) !== quote ||
- isEscaped(sectionEnd))) {
- sectionEnd += 1;
- }
- if (selector.charAt(sectionEnd) !== quote) {
- throw new Error("Attribute value didn't end");
- }
- value = unescapeCSS(selector.slice(selectorIndex + 1, sectionEnd));
- selectorIndex = sectionEnd + 1;
- }
- else {
- var valueStart = selectorIndex;
- while (selectorIndex < selector.length &&
- ((!isWhitespace(selector.charAt(selectorIndex)) &&
- selector.charAt(selectorIndex) !== "]") ||
- isEscaped(selectorIndex))) {
- selectorIndex += 1;
- }
- value = unescapeCSS(selector.slice(valueStart, selectorIndex));
- }
- stripWhitespace(0);
- // See if we have a force ignore flag
- var forceIgnore = selector.charAt(selectorIndex);
- // If the forceIgnore flag is set (either `i` or `s`), use that value
- if (forceIgnore === "s" || forceIgnore === "S") {
- ignoreCase = false;
- stripWhitespace(1);
- }
- else if (forceIgnore === "i" || forceIgnore === "I") {
- ignoreCase = true;
- stripWhitespace(1);
- }
- }
- // If `xmlMode` is set, there are no rules; otherwise, use the `caseInsensitiveAttributes` list.
- if (!options.xmlMode) {
- // TODO: Skip this for `exists`, as there is no value to compare to.
- ignoreCase !== null && ignoreCase !== void 0 ? ignoreCase : (ignoreCase = caseInsensitiveAttributes.has(name_2));
- }
- if (selector.charAt(selectorIndex) !== "]") {
- throw new Error("Attribute selector didn't terminate");
- }
- selectorIndex += 1;
- var attributeSelector = {
- type: "attribute",
- name: name_2,
- action: action,
- value: value,
- namespace: namespace,
- ignoreCase: ignoreCase,
- };
- tokens.push(attributeSelector);
- }
- else if (firstChar === ":") {
- if (selector.charAt(selectorIndex + 1) === ":") {
- tokens.push({
- type: "pseudo-element",
- name: getName(2).toLowerCase(),
- });
- continue;
- }
- var name_3 = getName(1).toLowerCase();
- var data = null;
- if (selector.charAt(selectorIndex) === "(") {
- if (unpackPseudos.has(name_3)) {
- if (quotes.has(selector.charAt(selectorIndex + 1))) {
- throw new Error("Pseudo-selector " + name_3 + " cannot be quoted");
- }
- data = [];
- selectorIndex = parseSelector(data, selector, options, selectorIndex + 1);
- if (selector.charAt(selectorIndex) !== ")") {
- throw new Error("Missing closing parenthesis in :" + name_3 + " (" + selector + ")");
- }
- selectorIndex += 1;
- }
- else {
- selectorIndex += 1;
- var start = selectorIndex;
- var counter = 1;
- for (; counter > 0 && selectorIndex < selector.length; selectorIndex++) {
- if (selector.charAt(selectorIndex) === "(" &&
- !isEscaped(selectorIndex)) {
- counter++;
- }
- else if (selector.charAt(selectorIndex) === ")" &&
- !isEscaped(selectorIndex)) {
- counter--;
- }
- }
- if (counter) {
- throw new Error("Parenthesis not matched");
- }
- data = selector.slice(start, selectorIndex - 1);
- if (stripQuotesFromPseudos.has(name_3)) {
- var quot = data.charAt(0);
- if (quot === data.slice(-1) && quotes.has(quot)) {
- data = data.slice(1, -1);
- }
- data = unescapeCSS(data);
- }
- }
- }
- tokens.push({ type: "pseudo", name: name_3, data: data });
- }
- else {
- var namespace = null;
- var name_4 = void 0;
- if (firstChar === "*") {
- selectorIndex += 1;
- name_4 = "*";
- }
- else if (reName.test(selector.slice(selectorIndex))) {
- if (selector.charAt(selectorIndex) === "|") {
- namespace = "";
- selectorIndex += 1;
- }
- name_4 = getName(0);
- }
- else {
- /*
- * We have finished parsing the selector.
- * Remove descendant tokens at the end if they exist,
- * and return the last index, so that parsing can be
- * picked up from here.
- */
- if (tokens.length &&
- tokens[tokens.length - 1].type === "descendant") {
- tokens.pop();
- }
- addToken(subselects, tokens);
- return selectorIndex;
- }
- if (selector.charAt(selectorIndex) === "|") {
- namespace = name_4;
- if (selector.charAt(selectorIndex + 1) === "*") {
- name_4 = "*";
- selectorIndex += 2;
- }
- else {
- name_4 = getName(1);
- }
- }
- if (name_4 === "*") {
- tokens.push({ type: "universal", namespace: namespace });
- }
- else {
- if ((_b = options.lowerCaseTags) !== null && _b !== void 0 ? _b : !options.xmlMode) {
- name_4 = name_4.toLowerCase();
- }
- tokens.push({ type: "tag", name: name_4, namespace: namespace });
- }
- }
- }
- }
- addToken(subselects, tokens);
- return selectorIndex;
- }
- function addToken(subselects, tokens) {
- if (subselects.length > 0 && tokens.length === 0) {
- throw new Error("Empty sub-selector");
- }
- subselects.push(tokens);
- }
|