aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/@asamuzakjp/nwsapi/src
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-13 21:34:48 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-13 21:34:48 -0800
commit76cb9c2a39d477a64824a985ade40507e3bbade1 (patch)
tree41e997aa9c6f538d3a136af61dae9424db2005a9 /vanilla/node_modules/@asamuzakjp/nwsapi/src
parent819a39a21ac992b1393244a4c283bbb125208c69 (diff)
downloadneko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.gz
neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.bz2
neko-76cb9c2a39d477a64824a985ade40507e3bbade1.zip
feat(vanilla): add testing infrastructure and tests (NK-wjnczv)
Diffstat (limited to 'vanilla/node_modules/@asamuzakjp/nwsapi/src')
-rw-r--r--vanilla/node_modules/@asamuzakjp/nwsapi/src/nwsapi.js1855
1 files changed, 1855 insertions, 0 deletions
diff --git a/vanilla/node_modules/@asamuzakjp/nwsapi/src/nwsapi.js b/vanilla/node_modules/@asamuzakjp/nwsapi/src/nwsapi.js
new file mode 100644
index 0000000..e118fd5
--- /dev/null
+++ b/vanilla/node_modules/@asamuzakjp/nwsapi/src/nwsapi.js
@@ -0,0 +1,1855 @@
+/**
+ * Forked and modified from nwsapi@2.2.2
+ * - Export to cjs only
+ * - Remove ./modules directory
+ * - Remove unused exported properties
+ * - Remove unused pseudo-classes
+ * - Remove Snapshot.root and resolve document.documentElement on runtime
+ * - Use `let` and `const` as much as possible
+ * - Use `===` and `!==`
+ * - Fix `:nth-of-type()`
+ * - Fix function source for :root, :target and :indeterminate pseudo-classes
+ * - Fix <ident-token>
+ * - Support complex selectors within `:is()` and `:not()`
+ * - Add ::slotted() and ::part() to pseudo-elements list
+ * - Add isContentEditable() function
+ * - Add createMatchingParensRegex() function from upstream
+ * - Invalidate cache for :has() pseudo class
+ * - Optimize some regular expressions
+ */
+/*
+ * Copyright (C) 2007-2019 Diego Perini
+ * All rights reserved.
+ *
+ * nwsapi.js - Fast CSS Selectors API Engine
+ *
+ * Author: Diego Perini <diego.perini at gmail com>
+ * Version: 2.2.0
+ * Created: 20070722
+ * Release: 20220901
+ *
+ * License:
+ * http://javascript.nwbox.com/nwsapi/MIT-LICENSE
+ * Download:
+ * http://javascript.nwbox.com/nwsapi/nwsapi.js
+ */
+
+(function Export(global, factory) {
+ 'use strict';
+ module.exports = factory;
+})(this, function Factory(global, Export) {
+ const version = 'nwsapi-2.2.2';
+
+ let doc = global.document;
+
+ /**
+ * Generate a regex that matches a balanced set of parentheses.
+ * Outermost parentheses are excluded so any amount of children can be handled.
+ * See https://stackoverflow.com/a/35271017 for reference
+ *
+ * @param {number} depth
+ * @return {string}
+ */
+ function createMatchingParensRegex(depth = 1) {
+ const out = '\\([^)(]*?(?:'.repeat(depth) + '\\([^)(]*?\\)' + '[^)(]*?)*?\\)'.repeat(depth);
+ // remove outermost escaped parens
+ return out.slice(2, out.length - 2);
+ }
+
+ const CFG = {
+ // extensions
+ operators: '[~*^$|]=|=',
+ combinators: '[\\s>+~](?=[^>+~])'
+ };
+
+ const NOT = {
+ // not enclosed in double/single/parens/square
+ doubleEnc: '(?=(?:[^"]*"[^"]*")*[^"]*$)',
+ singleEnc: "(?=(?:[^']*'[^']*')*[^']*$)",
+ parensEnc: '(?![^\\x28]*\\x29)',
+ squareEnc: '(?![^\\x5b]*\\x5d)'
+ };
+
+ const REX = {
+ // regular expressions
+ hasEscapes: /\\/,
+ hexNumbers: /^[0-9a-f]/i,
+ escOrQuote: /^\\|[\x22\x27]/,
+ regExpChar: /(?:(?!\\)[\\^$.*+?()[\]{}|/])/g,
+ trimSpaces: /[\r\n\f]|^\s+|\s+$/g,
+ commaGroup: RegExp('(\\s{0,255},\\s{0,255})' + NOT.squareEnc + NOT.parensEnc, 'g'),
+ splitGroup: /((?:\x28[^\x29]{0,255}\x29|\[[^\]]{0,255}\]|\\.|[^,])+)/g,
+ fixEscapes: /\\([0-9a-f]{1,6}\s?|.)|([\x22\x27])/gi,
+ combineWSP: RegExp('\\s{1,255}' + NOT.singleEnc + NOT.doubleEnc, 'g'),
+ tabCharWSP: RegExp('(\\s?\\t{1,255}\\s?)' + NOT.singleEnc + NOT.doubleEnc, 'g'),
+ pseudosWSP: RegExp('\\s{1,255}([-+])\\s{1,255}' + NOT.squareEnc, 'g')
+ };
+
+ const STD = {
+ combinator: /\s?([>+~])\s?/g,
+ apimethods: /^(?:[a-z]+|\*)\|/i,
+ namespaces: /(\*|[a-z]+)\|[-a-z]+/i
+ };
+
+ const GROUPS = {
+ // pseudo-classes requiring parameters
+ logicalsel: '(is|where|matches|not|has)(?:\\x28\\s?(' + createMatchingParensRegex(3) + ')\\s?\\x29)',
+ treestruct: '(nth(?:-last)?(?:-child|-of-type))(?:\\x28\\s?(even|odd|(?:[-+]?\\d*)(?:n\\s?[-+]?\\s?\\d*)?)\\s?(?:\\x29|$))',
+ // pseudo-classes not requiring parameters
+ locationpc: '(any-link|link|visited|target)\\b',
+ structural: '(root|empty|(?:(?:first|last|only)(?:-child|-of-type)))\\b',
+ inputstate: '(enabled|disabled|read-(?:only|write)|placeholder-shown|default)\\b',
+ inputvalue: '(checked|indeterminate)\\b',
+ // pseudo-classes for parsing only selectors
+ pseudoNop: '(autofill|-webkit-autofill)\\b',
+ // pseudo-elements starting with single colon (:)
+ pseudoSng: '(after|before|first-letter|first-line)\\b',
+ // pseudo-elements starting with double colon (::)
+ pseudoDbl: ':(after|before|first-letter|first-line|selection|part|placeholder|slotted|-webkit-[-a-z0-9]{2,})\\b'
+ };
+
+ const Patterns = {
+ // pseudo-classes
+ treestruct: RegExp('^:(?:' + GROUPS.treestruct + ')(.*)', 'i'),
+ structural: RegExp('^:(?:' + GROUPS.structural + ')(.*)', 'i'),
+ inputstate: RegExp('^:(?:' + GROUPS.inputstate + ')(.*)', 'i'),
+ inputvalue: RegExp('^:(?:' + GROUPS.inputvalue + ')(.*)', 'i'),
+ locationpc: RegExp('^:(?:' + GROUPS.locationpc + ')(.*)', 'i'),
+ logicalsel: RegExp('^:(?:' + GROUPS.logicalsel + ')(.*)', 'i'),
+ pseudoNop: RegExp('^:(?:' + GROUPS.pseudoNop + ')(.*)', 'i'),
+ pseudoSng: RegExp('^:(?:' + GROUPS.pseudoSng + ')(.*)', 'i'),
+ pseudoDbl: RegExp('^:(?:' + GROUPS.pseudoDbl + ')(.*)', 'i'),
+ // combinator symbols
+ children: /^\s?>\s?(.*)/,
+ adjacent: /^\s?\+\s?(.*)/,
+ relative: /^\s?~\s?(.*)/,
+ ancestor: /^\s+(.*)/,
+ // universal & namespace
+ universal: /^\*(.*)/,
+ namespace: /^(\w+|\*)?\|(.*)/
+ };
+
+ // emulate firefox error strings
+ const qsNotArgs = 'Not enough arguments';
+ const qsInvalid = ' is not a valid selector';
+
+ // detect structural pseudo-classes in selectors
+ const reNthElem = /(:nth(?:-last)?-child)/i;
+ const reNthType = /(:nth(?:-last)?-of-type)/i;
+
+ // placeholder for global regexp
+ let reOptimizer;
+ let reValidator;
+
+ // special handling configuration flags
+ const Config = {
+ IDS_DUPES: true,
+ MIXEDCASE: true,
+ LOGERRORS: true,
+ VERBOSITY: true
+ };
+
+ let NAMESPACE;
+ let QUIRKS_MODE;
+ let HTML_DOCUMENT;
+
+ const ATTR_STD_OPS = {
+ '=': 1,
+ '^=': 1,
+ '$=': 1,
+ '|=': 1,
+ '*=': 1,
+ '~=': 1
+ };
+
+ const HTML_TABLE = {
+ accept: 1,
+ 'accept-charset': 1,
+ align: 1,
+ alink: 1,
+ axis: 1,
+ bgcolor: 1,
+ charset: 1,
+ checked: 1,
+ clear: 1,
+ codetype: 1,
+ color: 1,
+ compact: 1,
+ declare: 1,
+ defer: 1,
+ dir: 1,
+ direction: 1,
+ disabled: 1,
+ enctype: 1,
+ face: 1,
+ frame: 1,
+ hreflang: 1,
+ 'http-equiv': 1,
+ lang: 1,
+ language: 1,
+ link: 1,
+ media: 1,
+ method: 1,
+ multiple: 1,
+ nohref: 1,
+ noresize: 1,
+ noshade: 1,
+ nowrap: 1,
+ readonly: 1,
+ rel: 1,
+ rev: 1,
+ rules: 1,
+ scope: 1,
+ scrolling: 1,
+ selected: 1,
+ shape: 1,
+ target: 1,
+ text: 1,
+ type: 1,
+ valign: 1,
+ valuetype: 1,
+ vlink: 1
+ };
+
+ const Combinators = {};
+
+ const Selectors = {};
+
+ const Operators = {
+ '=': {
+ p1: '^',
+ p2: '$',
+ p3: 'true'
+ },
+ '^=': {
+ p1: '^',
+ p2: '',
+ p3: 'true'
+ },
+ '$=': {
+ p1: '',
+ p2: '$',
+ p3: 'true'
+ },
+ '*=': {
+ p1: '',
+ p2: '',
+ p3: 'true'
+ },
+ '|=': {
+ p1: '^',
+ p2: '(-|$)',
+ p3: 'true'
+ },
+ '~=': {
+ p1: '(^|\\s)',
+ p2: '(\\s|$)',
+ p3: 'true'
+ }
+ };
+
+ const concatCall = function (nodes, callback) {
+ let i = 0;
+ const l = nodes.length;
+ const list = Array(l);
+ while (l > i) {
+ if (callback(list[i] = nodes[i]) === false) {
+ break;
+ }
+ ++i;
+ }
+ return list;
+ };
+
+ const concatList = function (list, nodes) {
+ let i = -1;
+ let l = nodes.length;
+ while (l--) {
+ list[list.length] = nodes[++i];
+ }
+ return list;
+ };
+
+ let hasDupes = false;
+
+ const documentOrder = function (a, b) {
+ if (!hasDupes && a === b) {
+ hasDupes = true;
+ return 0;
+ }
+ return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+ };
+
+ const unique = function (nodes) {
+ let i = 0;
+ let j = -1;
+ let l = nodes.length + 1;
+ const list = [];
+ while (--l) {
+ if (nodes[i++] === nodes[i]) {
+ continue;
+ }
+ list[++j] = nodes[i - 1];
+ }
+ hasDupes = false;
+ return list;
+ };
+
+ // check context for mixed content
+ const hasMixedCaseTagNames = function (context) {
+ const api = 'getElementsByTagNameNS';
+
+ // current host context (ownerDocument)
+ context = context.ownerDocument || context;
+
+ // documentElement (root) element namespace or default html/xhtml namespace
+ const ns = context.documentElement && context.documentElement.namespaceURI
+ ? context.documentElement.namespaceURI
+ : 'http://www.w3.org/1999/xhtml';
+
+ // checking the number of non HTML nodes in the document
+ return (context[api]('*', '*').length - context[api](ns, '*').length) > 0;
+ };
+
+ // check if the document type is HTML
+ const isHTML = function (node) {
+ const doc = node.ownerDocument || node;
+ return doc.nodeType === 9 && doc.contentType === 'text/html';
+ };
+
+ // convert single codepoint to UTF-16 encoding
+ const codePointToUTF16 = function (codePoint) {
+ // out of range, use replacement character
+ if (codePoint < 1 || codePoint > 0x10ffff ||
+ (codePoint > 0xd7ff && codePoint < 0xe000)) {
+ return '\\ufffd';
+ }
+ // javascript strings are UTF-16 encoded
+ if (codePoint < 0x10000) {
+ const lowHex = '000' + codePoint.toString(16);
+ return '\\u' + lowHex.substr(lowHex.length - 4);
+ }
+ // supplementary high + low surrogates
+ return '\\u' + (((codePoint - 0x10000) >> 0x0a) + 0xd800).toString(16) +
+ '\\u' + (((codePoint - 0x10000) % 0x400) + 0xdc00).toString(16);
+ };
+
+ // convert single codepoint to string
+ const stringFromCodePoint = function (codePoint) {
+ // out of range, use replacement character
+ if (codePoint < 1 || codePoint > 0x10ffff ||
+ (codePoint > 0xd7ff && codePoint < 0xe000)) {
+ return '\ufffd';
+ }
+ if (codePoint < 0x10000) {
+ return String.fromCharCode(codePoint);
+ }
+ return String.fromCodePoint(codePoint);
+ };
+
+ // convert escape sequence in a CSS string or identifier
+ // to javascript string with javascript escape sequences
+ const convertEscapes = function (str) {
+ return REX.hasEscapes.test(str)
+ ? str.replace(REX.fixEscapes, function (substring, p1, p2) {
+ // unescaped " or '
+ return p2
+ ? '\\' + p2
+ // javascript strings are UTF-16 encoded
+ : REX.hexNumbers.test(p1)
+ ? codePointToUTF16(parseInt(p1, 16))
+ // \' \"
+ : REX.escOrQuote.test(p1)
+ ? substring
+ // \g \h \. \# etc
+ : p1;
+ })
+ : str;
+ };
+
+ // convert escape sequence in a CSS string or identifier
+ // to javascript string with characters representations
+ const unescapeIdentifier = function (str) {
+ return REX.hasEscapes.test(str)
+ ? str.replace(REX.fixEscapes, function (substring, p1, p2) {
+ // unescaped " or '
+ return p2 || (REX.hexNumbers.test(p1)
+ ? stringFromCodePoint(parseInt(p1, 16))
+ // \' \"
+ : REX.escOrQuote.test(p1)
+ ? substring
+ // \g \h \. \# etc
+ : p1);
+ })
+ : str;
+ };
+
+ // empty set
+ const none = [];
+
+ // cached lambdas
+ const matchLambdas = {};
+ const selectLambdas = {};
+
+ // cached resolvers
+ let matchResolvers = {};
+ let selectResolvers = {};
+
+ const method = {
+ '#': 'getElementById',
+ '*': 'getElementsByTagName',
+ '|': 'getElementsByTagNameNS',
+ '.': 'getElementsByClassName'
+ };
+
+ // find duplicate ids using iterative walk
+ const byIdRaw = function (id, context) {
+ let node = context;
+ const nodes = [];
+ let next = node.firstElementChild;
+ while ((node = next)) {
+ node.id === id && nodes.push(node);
+ if ((next = node.firstElementChild || node.nextElementSibling)) {
+ continue;
+ }
+ while (!next && (node = node.parentElement) && node !== context) {
+ next = node.nextElementSibling;
+ }
+ }
+ return nodes;
+ };
+
+ // context agnostic getElementById
+ const byId = function (id, context) {
+ let e;
+ const api = method['#'];
+
+ // duplicates id allowed
+ if (Config.IDS_DUPES === false) {
+ if (api in context) {
+ e = context[api](id);
+ return e ? [e] : none;
+ }
+ } else if ('all' in context) {
+ if ((e = context.all[id])) {
+ if (e.nodeType === 1) {
+ return e.getAttribute('id') !== id ? [] : [e];
+ } else if (id === 'length') {
+ e = context[api](id);
+ return e ? [e] : none;
+ }
+ const nodes = [];
+ for (let i = 0, l = e.length; l > i; ++i) {
+ if (e[i].id === id) {
+ nodes.push(e[i]);
+ }
+ }
+ return nodes.length ? nodes : none;
+ } else {
+ return none;
+ }
+ }
+
+ return byIdRaw(id, context);
+ };
+
+ // context agnostic getElementsByTagName
+ const byTag = function (tag, context) {
+ let e;
+ let nodes;
+ const api = method['*'];
+
+ // DOCUMENT_NODE (9) & ELEMENT_NODE (1)
+ if (api in context) {
+ return Array.prototype.slice.call(context[api](tag));
+ } else {
+ tag = tag.toLowerCase();
+ // DOCUMENT_FRAGMENT_NODE (11)
+ if ((e = context.firstElementChild)) {
+ if (!(e.nextElementSibling || tag === '*' || e.localName === tag)) {
+ return Array.prototype.slice.call(e[api](tag));
+ } else {
+ nodes = [];
+ do {
+ if (tag === '*' || e.localName === tag) {
+ nodes.push(e);
+ }
+ concatList(nodes, e[api](tag));
+ } while ((e = e.nextElementSibling));
+ }
+ } else {
+ nodes = none;
+ }
+ }
+ return nodes;
+ };
+
+ // context agnostic getElementsByClassName
+ const byClass = function (cls, context) {
+ let e;
+ let nodes;
+ const api = method['.'];
+ let reCls;
+ // DOCUMENT_NODE (9) & ELEMENT_NODE (1)
+ if (api in context) {
+ return Array.prototype.slice.call(context[api](cls));
+ } else {
+ // DOCUMENT_FRAGMENT_NODE (11)
+ if ((e = context.firstElementChild)) {
+ reCls = RegExp('(^|\\s)' + cls + '(\\s|$)', QUIRKS_MODE ? 'i' : '');
+ if (!(e.nextElementSibling || reCls.test(e.className))) {
+ return Array.prototype.slice.call(e[api](cls));
+ } else {
+ nodes = [];
+ do {
+ if (reCls.test(e.className)) {
+ nodes.push(e);
+ }
+ concatList(nodes, e[api](cls));
+ } while ((e = e.nextElementSibling));
+ }
+ } else nodes = none;
+ }
+ return nodes;
+ };
+
+ const compat = {
+ '#': function (c, n) {
+ REX.hasEscapes.test(n) && (n = unescapeIdentifier(n));
+ return function (e, f) {
+ return byId(n, c);
+ };
+ },
+ '*': function (c, n) {
+ REX.hasEscapes.test(n) && (n = unescapeIdentifier(n));
+ return function (e, f) {
+ return byTag(n, c);
+ };
+ },
+ '|': function (c, n) {
+ REX.hasEscapes.test(n) && (n = unescapeIdentifier(n));
+ return function (e, f) {
+ return byTag(n, c);
+ };
+ },
+ '.': function (c, n) {
+ REX.hasEscapes.test(n) && (n = unescapeIdentifier(n));
+ return function (e, f) {
+ return byClass(n, c);
+ };
+ }
+ };
+
+ // namespace aware hasAttribute
+ // helper for XML/XHTML documents
+ const hasAttributeNS = function (e, name) {
+ let i;
+ let l;
+ const attr = e.getAttributeNames();
+ name = RegExp(':?' + name + '$', HTML_DOCUMENT ? 'i' : '');
+ for (i = 0, l = attr.length; l > i; ++i) {
+ if (name.test(attr[i])) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ // fast resolver for the :nth-child() and :nth-last-child() pseudo-classes
+ const nthElement = (function () {
+ let idx = 0;
+ let len = 0;
+ let set = 0;
+ let parent;
+ let parents = [];
+ let nodes = [];
+ return function (element, dir) {
+ // ensure caches are emptied after each run, invoking with dir = 2
+ if (dir === 2) {
+ idx = 0; len = 0; set = 0; nodes = []; parents = []; parent = undefined;
+ return -1;
+ }
+ let e, i, j, k, l;
+ if (parent === element.parentElement) {
+ i = set; j = idx; l = len;
+ } else {
+ l = parents.length;
+ parent = element.parentElement;
+ for (i = -1, j = 0, k = l - 1; l > j; ++j, --k) {
+ if (parents[j] === parent) {
+ i = j;
+ break;
+ }
+ if (parents[k] === parent) {
+ i = k;
+ break;
+ }
+ }
+ if (i < 0) {
+ parents[i = l] = parent;
+ l = 0; nodes[i] = [];
+ e = (parent && parent.firstElementChild) || element;
+ while (e) {
+ nodes[i][l] = e;
+ if (e === element) {
+ j = l;
+ }
+ e = e.nextElementSibling;
+ ++l;
+ }
+ set = i; idx = 0; len = l;
+ if (l < 2) {
+ return l;
+ }
+ } else {
+ l = nodes[i].length;
+ set = i;
+ }
+ }
+ if (element !== nodes[i][j] && element !== nodes[i][j = 0]) {
+ for (j = 0, e = nodes[i], k = l - 1; l > j; ++j, --k) {
+ if (e[j] === element) {
+ break;
+ }
+ if (e[k] === element) {
+ j = k;
+ break;
+ }
+ }
+ }
+ idx = j + 1; len = l;
+ return dir ? l - j : idx;
+ };
+ })();
+
+ // fast resolver for the :nth-of-type() and :nth-last-of-type() pseudo-classes
+ const nthOfType = (function () {
+ let idx = 0;
+ let len = 0;
+ let set = 0;
+ let parent;
+ let parents = [];
+ let nodes = [];
+ return function (element, dir) {
+ // ensure caches are emptied after each run, invoking with dir = 2
+ if (dir === 2) {
+ idx = 0; len = 0; set = 0; nodes = []; parents = []; parent = undefined;
+ return -1;
+ }
+ const name = element.localName;
+ const nsURI = element.namespaceURI;
+ if (nsURI !== 'http://www.w3.org/1999/xhtml') {
+ idx = 0; len = 0; set = 0; nodes = []; parents = []; parent = undefined;
+ }
+ let e;
+ let i;
+ let j;
+ let k;
+ let l;
+ if (nodes[set] && nodes[set][name] && parent === element.parentElement) {
+ i = set;
+ j = idx;
+ l = len;
+ } else {
+ l = parents.length;
+ parent = element.parentElement;
+ for (i = -1, j = 0, k = l - 1; l > j; ++j, --k) {
+ if (parents[j] === parent) {
+ i = j;
+ break;
+ }
+ if (parents[k] === parent) {
+ i = k;
+ break;
+ }
+ }
+ if (i < 0 || !nodes[i][name]) {
+ parents[i = l] = parent;
+ nodes[i] || (nodes[i] = Object());
+ l = 0; nodes[i][name] = [];
+ e = (parent && parent.firstElementChild) || element;
+ while (e) {
+ if (e === element) {
+ j = l;
+ }
+ if (e.localName === name && e.namespaceURI === nsURI) {
+ nodes[i][name][l] = e;
+ ++l;
+ }
+ e = e.nextElementSibling;
+ }
+ set = i; idx = j; len = l;
+ if (l < 2) {
+ return l;
+ }
+ } else {
+ l = nodes[i][name].length;
+ set = i;
+ }
+ }
+ if (element !== nodes[i][name][j] && element !== nodes[i][name][j = 0]) {
+ for (j = 0, e = nodes[i][name], k = l - 1; l > j; ++j, --k) {
+ if (e[j] === element) {
+ break;
+ }
+ if (e[k] === element) {
+ j = k;
+ break;
+ }
+ }
+ }
+ idx = j + 1; len = l;
+ return dir ? l - j : idx;
+ };
+ })();
+
+ // check if the node is the target
+ const isTarget = function (node) {
+ const doc = node.ownerDocument || node;
+ const { hash } = new URL(doc.URL);
+ if (node.id && hash === `#${node.id}` && doc.contains(node)) {
+ return true;
+ }
+ return false;
+ };
+
+ // check if node is indeterminate
+ const isIndeterminate = function (node) {
+ if ((node.indeterminate && node.localName === 'input' &&
+ node.type === 'checkbox') ||
+ (node.localName === 'progress' && !node.hasAttribute('value'))) {
+ return true;
+ }
+ if (node.localName === 'input' && node.type === 'radio' &&
+ !node.hasAttribute('checked')) {
+ const nodeName = node.name;
+ let parent = node.parentNode;
+ while (parent) {
+ if (parent.localName === 'form') {
+ break;
+ }
+ parent = parent.parentNode;
+ }
+ if (!parent) {
+ const doc = node.ownerDocument;
+ parent = doc.documentElement;
+ }
+ const items = parent.getElementsByTagName('input');
+ const l = items.length;
+ let checked;
+ for (let i = 0; i < l; i++) {
+ const item = items[i];
+ if (item.getAttribute('type') === 'radio') {
+ if (nodeName) {
+ if (item.getAttribute('name') === nodeName) {
+ checked = !!item.checked;
+ }
+ } else if (!item.hasAttribute('name')) {
+ checked = !!item.checked;
+ }
+ if (checked) {
+ break;
+ }
+ }
+ }
+ if (!checked) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ // check if node content is editable
+ const isContentEditable = function (node) {
+ let attrValue = 'inherit';
+ if (node.hasAttribute('contenteditable')) {
+ attrValue = node.getAttribute('contenteditable');
+ }
+ switch (attrValue) {
+ case '':
+ case 'plaintext-only':
+ case 'true':
+ return true;
+ case 'false':
+ return false;
+ default:
+ if (node.parentNode && node.parentNode.nodeType === 1) {
+ return isContentEditable(node.parentNode);
+ }
+ return false;
+ }
+ };
+
+ // build validation regexps used by the engine
+ const setIdentifierSyntax = function () {
+ //
+ // NOTE: SPECIAL CASES IN CSS SYNTAX PARSING RULES
+ //
+ // The <EOF-token> https://drafts.csswg.org/css-syntax/#typedef-eof-token
+ // allow mangled|unclosed selector syntax at the end of selectors strings
+ //
+ // Literal equivalent hex representations of the characters: " ' ` ] )
+ //
+ // \\x22 = " - double quotes \\x5b = [ - open square bracket
+ // \\x27 = ' - single quote \\x5d = ] - closed square bracket
+ // \\x60 = ` - back tick \\x28 = ( - open round parens
+ // \\x5c = \ - back slash \\x29 = ) - closed round parens
+ //
+ // using hex format prevents false matches of opened/closed instances
+ // pairs, coloring breakage and other editors highlightning problems.
+ //
+
+ // @see https://drafts.csswg.org/css-syntax-3/#ident-token-diagram
+ const nonascii = '[^\\x00-\\x9f]';
+ const esctoken = '\\\\(?:[^\\r\\n\\f\\da-f]|[\\da-f]{1,6}\\s{0,255})';
+ const identifier =
+ '(?:--|-?(?:[a-z_]|' + nonascii + '|' + esctoken + '))' +
+ '(?:[\\w-]|' + nonascii + '|' + esctoken + ')*';
+
+ const pseudonames = '[-\\w]+';
+ const pseudoparms = '(?:[-+]?\\d*)(?:n\\s?[-+]?\\s?\\d*)';
+ const doublequote = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*(?:"|$)';
+ const singlequote = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*(?:'|$)";
+
+ const attrparser = identifier + '|' + doublequote + '|' + singlequote;
+
+ const attrvalues = '([\\x22\\x27]?)((?!\\3)*|(?:\\\\?.)*?)(?:\\3|$)';
+
+ const attributes =
+ '\\[' +
+ // attribute presence
+ '(?:\\*\\|)?\\s?(' + identifier + '(?::' + identifier + ')?)\\s?' +
+ '(?:(' + CFG.operators + ')\\s?(?:' + attrparser + '))?' +
+ // attribute case sensitivity
+ '(?:\\s?\\b(i))?\\s?' +
+ '(?:\\]|$)';
+
+ const attrmatcher = attributes.replace(attrparser, attrvalues);
+
+ const pseudoclass =
+ '(?:\\x28\\s*' +
+ '(?:' + pseudoparms + '?)?|' +
+ // universal * &
+ // namespace *|*
+ '[*|]|' +
+ '(?:' +
+ '(?::' + pseudonames + '(?:\\x28' + pseudoparms + '?(?:\\x29|$))?)|' +
+ '(?:[.#]?' + identifier + ')|' +
+ '(?:' + attributes + ')' +
+ ')+|' +
+ '\\s?[>+~]\\s?|' +
+ '\\s?,\\s?|' +
+ '\\s|' +
+ '\\x29|$' +
+ ')*';
+
+ const standardValidator =
+ '(?=\\s?[^>+~(){}<])' +
+ '(?:' +
+ // universal * &
+ // namespace *|*
+ '\\*|\\||' +
+ '(?:[.#]?' + identifier + ')+|' +
+ '(?:' + attributes + ')+|' +
+ '(?:::?' + pseudonames + pseudoclass + ')|' +
+ '(?:\\s?' + CFG.combinators + '\\s?)|' +
+ '\\s?,\\s?|' +
+ '\\s?' +
+ ')+';
+
+ // the following global RE is used to return the
+ // deepest localName in selector strings and then
+ // use it to retrieve all possible matching nodes
+ // that will be filtered by compiled resolvers
+ reOptimizer = RegExp(
+ '(?:([.:#*]?)(' + identifier + ')' +
+ '(?::[-\\w]+|\\[[^\\]]+(?:\\]|$)|\\x28[^\\x29]+(?:\\x29|$))*' +
+ ')$', 'i');
+
+ // global
+ reValidator = RegExp(standardValidator, 'gi');
+
+ Patterns.id = RegExp('^#(' + identifier + ')(.*)', 'i');
+ Patterns.tagName = RegExp('^(' + identifier + ')(.*)', 'i');
+ Patterns.className = RegExp('^\\.(' + identifier + ')(.*)', 'i');
+ Patterns.attribute = RegExp('^(?:' + attrmatcher + ')(.*)');
+ };
+
+ // configure the engine to use special handling
+ const configure = function (option, clear) {
+ if (typeof option === 'string') {
+ return !!Config[option];
+ }
+ if (typeof option !== 'object') {
+ return Config;
+ }
+ for (const i in option) {
+ Config[i] = !!option[i];
+ }
+ // clear lambda cache
+ if (clear) {
+ matchResolvers = {};
+ selectResolvers = {};
+ }
+ setIdentifierSyntax();
+ return true;
+ };
+
+ // centralized error and exceptions handling
+ const emit = function (message, proto) {
+ let err;
+ if (Config.VERBOSITY) {
+ if (global[proto]) {
+ err = new global[proto](message);
+ } else {
+ err = new global.DOMException(message, 'SyntaxError');
+ }
+ throw err;
+ }
+ if (Config.LOGERRORS && console && console.log) {
+ console.log(message);
+ }
+ };
+
+ // passed to resolvers
+ const Snapshot = {
+ doc: null,
+ from: null,
+ byTag: null,
+ first: null,
+ match: null,
+ ancestor: null,
+ nthOfType: null,
+ nthElement: null,
+ hasAttributeNS: null,
+ isTarget: null,
+ isIndeterminate: null,
+ isContentEditable: null
+ };
+
+ // context
+ let lastContext;
+
+ const switchContext = function (context, force) {
+ const oldDoc = doc;
+ doc = context.ownerDocument || context;
+ if (force || oldDoc !== doc) {
+ // force a new check for each document change
+ // performed before the next select operation
+ HTML_DOCUMENT = isHTML(doc);
+ QUIRKS_MODE = HTML_DOCUMENT && doc.compatMode.indexOf('CSS') < 0;
+ NAMESPACE = doc.documentElement && doc.documentElement.namespaceURI;
+ Snapshot.doc = doc;
+ }
+ Snapshot.from = context;
+ return context;
+ };
+
+ // selector
+ let lastMatched;
+ let lastSelected;
+
+ const F_INIT = '"use strict";return function Resolver(c,f,x,r)';
+
+ const S_HEAD = 'var e,n,o,j=r.length-1,k=-1';
+ const M_HEAD = 'var e,n,o';
+
+ const S_LOOP = 'main:while((e=c[++k]))';
+ const N_LOOP = 'main:while((e=c.item(++k)))';
+ const M_LOOP = 'e=c;';
+
+ const S_BODY = 'r[++j]=c[k];';
+ const N_BODY = 'r[++j]=c.item(k);';
+ const M_BODY = '';
+
+ const S_TAIL = 'continue main;';
+ const M_TAIL = 'r=true;';
+
+ const S_TEST = 'if(f(c[k])){break main;}';
+ const N_TEST = 'if(f(c.item(k))){break main;}';
+ const M_TEST = 'f(c);';
+
+ let S_VARS = [];
+ let M_VARS = [];
+
+ // build conditional code to check components of selector strings
+ const compileSelector = function (expression, source, mode, callback) {
+ // N is the negation pseudo-class flag
+ // D is the default inverted negation flag
+ let a;
+ let b;
+ let n;
+ let f;
+ let name;
+ let NS;
+ const N = '';
+ const D = '!';
+ let compat;
+ let expr;
+ let match;
+ let result;
+ let status;
+ let symbol;
+ let test;
+ let type;
+ let selector = expression;
+ let vars;
+
+ // original 'select' or 'match' selector string before normalization
+ const selectorString = mode ? lastSelected : lastMatched;
+
+ // isolate selector combinators/components and normalize whitespace
+ selector = selector.replace(STD.combinator, '$1'); // .replace(STD.whitespace, ' ');
+
+ let selectorRecursion = true;
+ while (selector) {
+ // get namespace prefix if present or get first char of selector
+ symbol = STD.apimethods.test(selector) ? '|' : selector[0];
+
+ switch (symbol) {
+ // universal resolver
+ case '*':
+ match = selector.match(Patterns.universal);
+ if (N === '!') {
+ source = 'if(' + N + 'true' + '){' + source + '}';
+ }
+ break;
+ // id resolver
+ case '#':
+ match = selector.match(Patterns.id);
+ source = 'if(' + N + '(/^' + match[1] + '$/.test(e.getAttribute("id"))' +
+ ')){' + source + '}';
+ break;
+ // class name resolver
+ case '.':
+ match = selector.match(Patterns.className);
+ compat = (QUIRKS_MODE ? 'i' : '') + '.test(e.getAttribute("class"))';
+ source = 'if(' + N + '(/(^|\\s)' + match[1] + '(\\s|$)/' + compat +
+ ')){' + source + '}';
+ break;
+ // tag name resolver
+ case (/[_a-z]/i.test(symbol) ? symbol : undefined):
+ match = selector.match(Patterns.tagName);
+ source = 'if(' + N + '(e.localName' +
+ (Config.MIXEDCASE || hasMixedCaseTagNames(doc)
+ ? '=="' + match[1].toLowerCase() + '"'
+ : '=="' + match[1].toUpperCase() + '"') +
+ ')){' + source + '}';
+ break;
+ // namespace resolver
+ case '|':
+ match = selector.match(Patterns.namespace);
+ if (match[1] === '*') {
+ source = 'if(' + N + 'true){' + source + '}';
+ } else if (!match[1]) {
+ source = 'if(' + N + '(!e.namespaceURI)){' + source + '}';
+ } else if (typeof match[1] === 'string' && doc.documentElement &&
+ doc.documentElement.prefix === match[1]) {
+ source = 'if(' + N + '(e.namespaceURI=="' + NAMESPACE + '")){' + source + '}';
+ } else {
+ emit('\'' + selectorString + '\'' + qsInvalid);
+ }
+ break;
+ // attributes resolver
+ case '[':
+ match = selector.match(Patterns.attribute);
+ NS = match[0].match(STD.namespaces);
+ name = match[1];
+ expr = name.split(':');
+ expr = expr.length === 2 ? expr[1] : expr[0];
+ if (match[2] && !(test = Operators[match[2]])) {
+ emit('\'' + selectorString + '\'' + qsInvalid);
+ return '';
+ }
+ if (match[4] === '') {
+ test = match[2] === '~='
+ ? { p1: '^\\s', p2: '+$', p3: 'true' }
+ : match[2] in ATTR_STD_OPS && match[2] !== '~='
+ ? { p1: '^', p2: '$', p3: 'true' }
+ : test;
+ } else if (match[2] === '~=' && match[4].includes(' ')) {
+ // whitespace separated list but value contains space
+ source = 'if(' + N + 'false){' + source + '}';
+ break;
+ } else if (match[4]) {
+ match[4] = convertEscapes(match[4]).replace(REX.regExpChar, '\\$&');
+ }
+ type = match[5] === 'i' || (HTML_DOCUMENT && HTML_TABLE[expr.toLowerCase()])
+ ? 'i'
+ : '';
+ source =
+ 'if(' + N + '(' +
+ (!match[2]
+ ? (NS ? 's.hasAttributeNS(e,"' + name + '")' : 'e.hasAttribute&&e.hasAttribute("' + name + '")')
+ : !match[4] && ATTR_STD_OPS[match[2]] && match[2] !== '~='
+ ? 'e.getAttribute&&e.getAttribute("' + name + '")==""'
+ : '(/' + test.p1 + match[4] + test.p2 + '/' + type + ').test(e.getAttribute&&e.getAttribute("' + name + '"))==' + test.p3) +
+ ')){' + source + '}';
+ break;
+ // *** General sibling combinator
+ // E ~ F (F relative sibling of E)
+ case '~':
+ match = selector.match(Patterns.relative);
+ source = 'n=e;while((e=e.previousElementSibling)){' + source + '}e=n;';
+ break;
+ // *** Adjacent sibling combinator
+ // E + F (F adiacent sibling of E)
+ case '+':
+ match = selector.match(Patterns.adjacent);
+ source = 'n=e;if((e=e.previousElementSibling)){' + source + '}e=n;';
+ break;
+ // *** Descendant combinator
+ // E F (E ancestor of F)
+ case '\x09':
+ case '\x20':
+ match = selector.match(Patterns.ancestor);
+ source = 'n=e;while((e=e.parentElement)){' + source + '}e=n;';
+ break;
+ // *** Child combinator
+ // E > F (F children of E)
+ case '>':
+ match = selector.match(Patterns.children);
+ source = 'n=e;if((e=e.parentElement)){' + source + '}e=n;';
+ break;
+ // *** user supplied combinators extensions
+ case (symbol in Combinators ? symbol : undefined):
+ // for other registered combinators extensions
+ match[match.length - 1] = '*';
+ source = Combinators[symbol](match) + source;
+ break;
+ // *** tree-structural pseudo-classes
+ // :root, :empty, :first-child, :last-child, :only-child, :first-of-type, :last-of-type, :only-of-type
+ case ':':
+ if ((match = selector.match(Patterns.structural))) {
+ match[1] = match[1].toLowerCase();
+ switch (match[1]) {
+ case 'root':
+ // there can only be one :root element, so exit the loop once found
+ source = 'if(' + N + '(e===s.doc.documentElement)){' + source + (mode ? 'break main;' : '') + '}';
+ break;
+ case 'empty':
+ // matches elements that don't contain elements or text nodes
+ source = 'n=e.firstChild;while(n&&!(/1|3/).test(n.nodeType)){n=n.nextSibling}if(' + D + 'n){' + source + '}';
+ break;
+ // *** child-indexed pseudo-classes
+ // :first-child, :last-child, :only-child
+ case 'only-child':
+ source = 'if(' + N + '(!e.nextElementSibling&&!e.previousElementSibling)){' + source + '}';
+ break;
+ case 'last-child':
+ source = 'if(' + N + '(!e.nextElementSibling)){' + source + '}';
+ break;
+ case 'first-child':
+ source = 'if(' + N + '(!e.previousElementSibling)){' + source + '}';
+ break;
+ // *** typed child-indexed pseudo-classes
+ // :only-of-type, :last-of-type, :first-of-type
+ case 'only-of-type':
+ source = 'o=e.localName;' +
+ 'n=e;while((n=n.nextElementSibling)&&n.localName!=o);if(!n){' +
+ 'n=e;while((n=n.previousElementSibling)&&n.localName!=o);}if(' + D + 'n){' + source + '}';
+ break;
+ case 'last-of-type':
+ source = 'n=e;o=e.localName;while((n=n.nextElementSibling)&&n.localName!=o);if(' + D + 'n){' + source + '}';
+ break;
+ case 'first-of-type':
+ source = 'n=e;o=e.localName;while((n=n.previousElementSibling)&&n.localName!=o);if(' + D + 'n){' + source + '}';
+ break;
+ default:
+ emit('\'' + selectorString + '\'' + qsInvalid);
+ }
+ // *** child-indexed & typed child-indexed pseudo-classes
+ // :nth-child, :nth-of-type, :nth-last-child, :nth-last-of-type
+ } else if ((match = selector.match(Patterns.treestruct))) {
+ match[1] = match[1].toLowerCase();
+ switch (match[1]) {
+ case 'nth-child':
+ case 'nth-of-type':
+ case 'nth-last-child':
+ case 'nth-last-of-type':
+ expr = /-of-type/i.test(match[1]);
+ if (match[1] && match[2]) {
+ type = /last/i.test(match[1]);
+ if (match[2] === 'n') {
+ source = 'if(' + N + 'true){' + source + '}';
+ break;
+ } else if (match[2] === '1') {
+ test = type ? 'next' : 'previous';
+ source = expr
+ ? 'n=e;o=e.localName;' +
+ 'while((n=n.' + test + 'ElementSibling)&&n.localName!=o);if(' + D + 'n){' + source + '}'
+ : 'if(' + N + '!e.' + test + 'ElementSibling){' + source + '}';
+ break;
+ } else if (match[2] === 'even' || match[2] === '2n0' || match[2] === '2n+0' || match[2] === '2n') {
+ test = 'n%2==0';
+ } else if (match[2] === 'odd' || match[2] === '2n1' || match[2] === '2n+1') {
+ test = 'n%2==1';
+ } else {
+ f = /n/i.test(match[2]);
+ n = match[2].split('n');
+ a = parseInt(n[0], 10) || 0;
+ b = parseInt(n[1], 10) || 0;
+ if (n[0] === '-') {
+ a = -1;
+ }
+ if (n[0] === '+') {
+ a = +1;
+ }
+ test = (b ? '(n' + (b > 0 ? '-' : '+') + Math.abs(b) + ')' : 'n') + '%' + a + '==0';
+ test = a >= +1
+ ? (f
+ ? 'n>' + (b - 1) + (Math.abs(a) !== 1
+ ? '&&' + test
+ : '')
+ : 'n==' + a)
+ : a <= -1
+ ? (f
+ ? 'n<' + (b + 1) + (Math.abs(a) !== 1
+ ? '&&' + test
+ : '')
+ : 'n==' + a)
+ : a === 0
+ ? (n[0]
+ ? 'n==' + b
+ : 'n>' + (b - 1))
+ : 'false';
+ }
+ expr = expr ? 'OfType' : 'Element';
+ type = type ? 'true' : 'false';
+ source = 'n=s.nth' + expr + '(e,' + type + ');if(' + N + '(' + test + ')){' + source + '}';
+ } else {
+ emit('\'' + selectorString + '\'' + qsInvalid);
+ }
+ break;
+ default:
+ emit('\'' + selectorString + '\'' + qsInvalid);
+ }
+ // *** logical combination pseudo-classes
+ // :is( s1, [ s2, ... ]), :not( s1, [ s2, ... ])
+ } else if ((match = selector.match(Patterns.logicalsel))) {
+ match[1] = match[1].toLowerCase();
+ expr = match[2].replace(REX.CommaGroup, ',').replace(REX.TrimSpaces, '');
+ switch (match[1]) {
+ // FIXME:
+ case 'is':
+ case 'where':
+ case 'matches':
+ source = 'if(s.match("' + expr.replace(/\x22/g, '\\"') + '",e)){' + source + '}';
+ break;
+ // FIXME:
+ case 'not':
+ source = 'if(!s.match("' + expr.replace(/\x22/g, '\\"') + '",e)){' + source + '}';
+ break;
+ // FIXME:
+ case 'has':
+ // clear cache
+ matchResolvers = {};
+ source = 'if(e.querySelector(":scope ' + expr.replace(/\x22/g, '\\"') + '")){' + source + '}';
+ break;
+ default:
+ emit('\'' + selectorString + '\'' + qsInvalid);
+ }
+ // *** location pseudo-classes
+ // :any-link, :link, :visited, :target
+ } else if ((match = selector.match(Patterns.locationpc))) {
+ match[1] = match[1].toLowerCase();
+ switch (match[1]) {
+ case 'any-link':
+ source = 'if(' + N + '(/^a|area$/i.test(e.localName)&&e.hasAttribute("href")||e.visited)){' + source + '}';
+ break;
+ case 'link':
+ source = 'if(' + N + '(/^a|area$/i.test(e.localName)&&e.hasAttribute("href"))){' + source + '}';
+ break;
+ // FIXME:
+ case 'visited':
+ source = 'if(' + N + '(/^a|area$/i.test(e.localName)&&e.hasAttribute("href")&&e.visited)){' + source + '}';
+ break;
+ case 'target':
+ source = 'if(s.isTarget(e)){' + source + '}';
+ break;
+ default:
+ emit('\'' + selectorString + '\'' + qsInvalid);
+ }
+ // *** user interface and form pseudo-classes
+ // :enabled, :disabled, :read-only, :read-write, :placeholder-shown, :default
+ } else if ((match = selector.match(Patterns.inputstate))) {
+ match[1] = match[1].toLowerCase();
+ switch (match[1]) {
+ // FIXME: lacks custom element support
+ case 'enabled':
+ source = 'if((("form" in e||/^optgroup$/i.test(e.localName))&&"disabled" in e &&e.disabled===false' +
+ ')){' + source + '}';
+ break;
+ // FIXME: lacks custom element support
+ case 'disabled':
+ // https://html.spec.whatwg.org/#enabling-and-disabling-form-controls:-the-disabled-attribute
+ source = 'if((("form" in e||/^optgroup$/i.test(e.localName))&&"disabled" in e)){' +
+ // F is true if any of the fieldset elements in the ancestry chain has the disabled attribute specified
+ // L is true if the first legend element of the fieldset contains the element
+ 'var x=0,N=[],F=false,L=false;' +
+ 'if(!(/^(optgroup|option)$/i.test(e.localName))){' +
+ 'n=e.parentElement;' +
+ 'while(n){' +
+ 'if(n.localName==="fieldset"){' +
+ 'N[x++]=n;' +
+ 'if(n.disabled===true){' +
+ 'F=true;' +
+ 'break;' +
+ '}' +
+ '}' +
+ 'n=n.parentElement;' +
+ '}' +
+ 'for(var x=0;x<N.length;x++){' +
+ 'if((n=s.first("legend",N[x]))&&n.contains(e)){' +
+ 'L=true;' +
+ 'break;' +
+ '}' +
+ '}' +
+ '}' +
+ 'if(e.disabled===true||(F&&!L)){' + source + '}}';
+ break;
+ case 'read-only':
+ source =
+ 'if(' +
+ '(/^textarea$/i.test(e.localName)&&(e.readOnly||e.disabled))||' +
+ '(/^input$/i.test(e.localName)&&("|date|datetime-local|email|month|number|password|search|tel|text|time|url|week|".includes("|"+e.type+"|")?(e.readOnly||e.disabled):true))||' +
+ '(!/^(?:input|textarea)$/i.test(e.localName) && !s.isContentEditable(e))' +
+ '){' + source + '}';
+ break;
+ case 'read-write':
+ source =
+ 'if(' +
+ '(/^textarea$/i.test(e.localName)&&!e.readOnly&&!e.disabled)||' +
+ '(/^input$/i.test(e.localName)&&"|date|datetime-local|email|month|number|password|search|tel|text|time|url|week|".includes("|"+e.type+"|")&&!e.readOnly&&!e.disabled)||' +
+ '(!/^(?:input|textarea)$/i.test(e.localName) && s.isContentEditable(e))' +
+ '){' + source + '}';
+ break;
+ // FIXME:
+ case 'placeholder-shown':
+ source =
+ 'if((' +
+ '(/^input|textarea$/i.test(e.localName))&&e.hasAttribute("placeholder")&&' +
+ '("|textarea|password|number|search|email|text|tel|url|".includes("|"+e.type+"|"))&&' +
+ '(!s.match(":focus",e))' +
+ ')){' + source + '}';
+ break;
+ // FIXME:
+ case 'default':
+ source =
+ 'if(("form" in e && e.form)){' +
+ 'var x=0;n=[];' +
+ 'if(e.type=="image")n=e.form.getElementsByTagName("input");' +
+ 'if(e.type=="submit")n=e.form.elements;' +
+ 'while(n[x]&&e!==n[x]){' +
+ 'if(n[x].type=="image")break;' +
+ 'if(n[x].type=="submit")break;' +
+ 'x++;' +
+ '}' +
+ '}' +
+ 'if((e.form&&(e===n[x]&&"|image|submit|".includes("|"+e.type+"|"))||' +
+ '((/^option$/i.test(e.localName))&&e.defaultSelected)||' +
+ '(("|radio|checkbox|".includes("|"+e.type+"|"))&&e.defaultChecked)' +
+ ')){' + source + '}';
+ break;
+ default:
+ emit('\'' + selector_string + '\'' + qsInvalid);
+ break;
+ }
+ // *** input pseudo-classes (for form validation)
+ // :checked, :indeterminate, :valid, :invalid, :in-range, :out-of-range, :required, :optional
+ } else if ((match = selector.match(Patterns.inputvalue))) {
+ match[1] = match[1].toLowerCase();
+ switch (match[1]) {
+ case 'checked':
+ source = 'if(' + N + '(/^input$/i.test(e.localName)&&' +
+ '("|radio|checkbox|".includes("|"+e.type+"|")&&e.checked)||' +
+ '(/^option$/i.test(e.localName)&&(e.selected||e.checked))' +
+ ')){' + source + '}';
+ break;
+ case 'indeterminate':
+ source = 'if(s.isIndeterminate(e)){' + source + '}';
+ break;
+ // FIXME:
+ case 'required':
+ source =
+ 'if(' + N +
+ '(/^input|select|textarea$/i.test(e.localName)&&e.required)' +
+ '){' + source + '}';
+ break;
+ // FIXME:
+ case 'optional':
+ source =
+ 'if(' + N +
+ '(/^input|select|textarea$/i.test(e.localName)&&!e.required)' +
+ '){' + source + '}';
+ break;
+ // FIXME:
+ case 'invalid':
+ source =
+ 'if(' + N + '((' +
+ '(/^form$/i.test(e.localName)&&!e.noValidate)||' +
+ '(e.willValidate&&!e.formNoValidate))&&!e.checkValidity())||' +
+ '(/^fieldset$/i.test(e.localName)&&s.first(":invalid",e))' +
+ '){' + source + '}';
+ break;
+ // FIXME:
+ case 'valid':
+ source =
+ 'if(' + N + '((' +
+ '(/^form$/i.test(e.localName)&&!e.noValidate)||' +
+ '(e.willValidate&&!e.formNoValidate))&&e.checkValidity())||' +
+ '(/^fieldset$/i.test(e.localName)&&s.first(":valid",e))' +
+ '){' + source + '}';
+ break;
+ // FIXME:
+ case 'in-range':
+ source =
+ 'if(' + N +
+ '(/^input$/i.test(e.localName))&&' +
+ '(e.willValidate&&!e.formNoValidate)&&' +
+ '(!e.validity.rangeUnderflow&&!e.validity.rangeOverflow)&&' +
+ '("|date|datetime-local|month|number|range|time|week|".includes("|"+e.type+"|"))&&' +
+ '("range"==e.type||e.getAttribute("min")||e.getAttribute("max"))' +
+ '){' + source + '}';
+ break;
+ // FIXME:
+ case 'out-of-range':
+ source =
+ 'if(' + N +
+ '(/^input$/i.test(e.localName))&&' +
+ '(e.willValidate&&!e.formNoValidate)&&' +
+ '(e.validity.rangeUnderflow||e.validity.rangeOverflow)&&' +
+ '("|date|datetime-local|month|number|range|time|week|".includes("|"+e.type+"|"))&&' +
+ '("range"==e.type||e.getAttribute("min")||e.getAttribute("max"))' +
+ '){' + source + '}';
+ break;
+ default:
+ emit('\'' + selectorString + '\'' + qsInvalid);
+ }
+ // allow pseudo-elements starting with single colon (:)
+ // :after, :before, :first-letter, :first-line
+ // assert: e.type is in double-colon format, like ::after
+ } else if ((match = selector.match(Patterns.pseudoSng))) {
+ source = 'if(e.element&&e.type.toLowerCase()=="' +
+ ':' + match[0].toLowerCase() + '"){e=e.element;' + source + '}';
+ // allow pseudo-elements starting with double colon (::)
+ // ::after, ::before, ::marker, ::placeholder, ::inactive-selection, ::selection, ::-webkit-<foo-bar>
+ // assert: e.type is in double-colon format, like ::after
+ } else if ((match = selector.match(Patterns.pseudoDbl))) {
+ source = 'if(e.element&&e.type.toLowerCase()=="' +
+ match[0].toLowerCase() + '"){e=e.element;' + source + '}';
+ // placeholder for parsed only no-op selectors
+ } else if ((match = selector.match(Patterns.pseudoNop))) {
+ source = 'if(' + N + 'false' + '){' + source + '}';
+ } else {
+ // reset
+ expr = false;
+ status = false;
+ // process registered selector extensions
+ for (expr in Selectors) {
+ if ((match = selector.match(Selectors[expr].Expression))) {
+ result = Selectors[expr].Callback(match, source, mode, callback);
+ if ('match' in result) {
+ match = result.match;
+ }
+ vars = result.modvar;
+ if (mode) {
+ // add extra select() vars
+ vars && !S_VARS.includes(vars) && S_VARS.push(vars);
+ } else {
+ // add extra match() vars
+ vars && M_VARS.includes(vars) && M_VARS.push(vars);
+ }
+ // extension source code
+ source = result.source;
+ // extension status code
+ status = result.status;
+ // break on status error
+ if (status) { break; }
+ }
+ }
+ if (!status) {
+ emit('unknown pseudo-class selector \'' + selector + '\'');
+ return '';
+ }
+ if (!expr) {
+ emit('unknown token in selector \'' + selector + '\'');
+ return '';
+ }
+ }
+ break;
+ default:
+ selectorRecursion = false;
+ emit('\'' + selectorString + '\'' + qsInvalid);
+ }
+ // end of switch symbol
+ if (!selectorRecursion) {
+ break;
+ }
+ if (!match) {
+ emit('\'' + selectorString + '\'' + qsInvalid);
+ return '';
+ }
+
+ // pop last component
+ selector = match.pop();
+ }
+ // end of while selector
+
+ return source;
+ };
+
+ // compile groups or single selector strings into
+ // executable functions for matching or selecting
+ const compile = function (selector, mode, callback) {
+ let head = ''; let loop = ''; let macro = ''; let source = ''; let vars = '';
+
+ // 'mode' can be boolean or null
+ // true = select / false = match
+ // null to use collection.item()
+ switch (mode) {
+ case true:
+ if (selectLambdas[selector]) {
+ return selectLambdas[selector];
+ }
+ macro = S_BODY + (callback ? S_TEST : '') + S_TAIL;
+ head = S_HEAD;
+ loop = S_LOOP;
+ break;
+ case false:
+ if (matchLambdas[selector]) {
+ return matchLambdas[selector];
+ }
+ macro = M_BODY + (callback ? M_TEST : '') + M_TAIL;
+ head = M_HEAD;
+ loop = M_LOOP;
+ break;
+ case null:
+ if (selectLambdas[selector]) {
+ return selectLambdas[selector];
+ }
+ macro = N_BODY + (callback ? N_TEST : '') + S_TAIL;
+ head = S_HEAD;
+ loop = N_LOOP;
+ break;
+ default:
+ }
+
+ source = compileSelector(selector, macro, mode, callback);
+
+ loop += (mode || mode === null) ? '{' + source + '}' : source;
+
+ if ((mode || mode === null) && selector.includes(':nth')) {
+ loop += reNthElem.test(selector) ? 's.nthElement(null, 2);' : '';
+ loop += reNthType.test(selector) ? 's.nthOfType(null, 2);' : '';
+ }
+
+ if (S_VARS[0] || M_VARS[0]) {
+ vars = ',' + (S_VARS.join(',') || M_VARS.join(','));
+ S_VARS = [];
+ M_VARS = [];
+ }
+
+ const factory = Function('s', F_INIT + '{' + head + vars + ';' + loop + 'return r;}')(Snapshot);
+
+ return mode || mode === null ? (selectLambdas[selector] = factory) : (matchLambdas[selector] = factory);
+ };
+
+ // optimize selectors avoiding duplicated checks
+ const optimize = function (selector, token) {
+ const index = token.index;
+ const length = token[1].length + token[2].length;
+ return selector.slice(0, index) +
+ (' >+~'.indexOf(selector.charAt(index - 1)) > -1
+ ? (':['.indexOf(selector.charAt(index + length + 1)) > -1
+ ? '*'
+ : '')
+ : '') + selector.slice(index + length - (token[1] === '*' ? 1 : 0));
+ };
+
+ // prepare factory resolvers and closure collections
+ const collect = function (selectors, context, callback) {
+ let i;
+ let l;
+ const seen = { };
+ let token = ['', '*', '*'];
+ const optimized = selectors;
+ const factory = [];
+ const htmlset = [];
+ const nodeset = [];
+ let results = [];
+ let type;
+
+ for (i = 0, l = selectors.length; l > i; ++i) {
+ if (!seen[selectors[i]] && (seen[selectors[i]] = true)) {
+ type = selectors[i].match(reOptimizer);
+ if (type && type[1] !== ':' && (token = type)) {
+ token[1] || (token[1] = '*');
+ optimized[i] = optimize(optimized[i], token);
+ } else {
+ token = ['', '*', '*'];
+ }
+ }
+
+ nodeset[i] = token[1] + token[2];
+ htmlset[i] = compat[token[1]](context, token[2]);
+ factory[i] = compile(optimized[i], true, null);
+
+ factory[i]
+ ? factory[i](htmlset[i](), callback, context, results)
+ : results.concat(htmlset[i]());
+ }
+
+ if (l > 1) {
+ results.sort(documentOrder);
+ hasDupes && (results = unique(results));
+ }
+
+ return {
+ callback,
+ context,
+ factory,
+ htmlset,
+ nodeset,
+ results
+ };
+ };
+
+ // replace ':scope' pseudo-class with element references
+ const makeref = function (selectors, element) {
+ // DOCUMENT_NODE (9)
+ if (element.nodeType === 9) {
+ element = element.documentElement;
+ }
+
+ return selectors.replace(/:scope/gi,
+ element.localName +
+ (element.id ? '#' + element.id : '') +
+ (element.className ? '.' + element.classList[0] : ''));
+ };
+
+ const matchAssert = function (f, element, callback) {
+ let r = false;
+ for (let i = 0, l = f.length; l > i; ++i) {
+ f[i](element, callback, null, false) && (r = true);
+ }
+ return r;
+ };
+
+ const matchCollect = function (selectors, callback) {
+ const f = [];
+ for (let i = 0, l = selectors.length; l > i; ++i) {
+ f[i] = compile(selectors[i], false, callback);
+ }
+ return { factory: f };
+ };
+
+ // equivalent of w3c 'matches' method
+ const match = function _matches(selectors, element, callback) {
+ let expressions;
+
+ if (element && !/:has\(/.test(selectors) && matchResolvers[selectors]) {
+ return matchAssert(matchResolvers[selectors].factory, element, callback);
+ }
+
+ lastMatched = selectors;
+
+ // arguments validation
+ if (arguments.length === 0) {
+ emit(qsNotArgs, 'TypeError');
+ return Config.VERBOSITY ? undefined : false;
+ } else if (arguments[0] === '') {
+ emit('\'\'' + qsInvalid);
+ return Config.VERBOSITY ? undefined : false;
+ }
+
+ // input NULL or UNDEFINED
+ if (typeof selectors !== 'string') {
+ selectors = '' + selectors;
+ }
+
+ if ((/:scope/i).test(selectors)) {
+ selectors = makeref(selectors, element);
+ }
+
+ // normalize input string
+ const parsed = selectors
+ .replace(/\0|\\$/g, '\ufffd')
+ .replace(REX.combineWSP, '\x20')
+ .replace(REX.pseudosWSP, '$1')
+ .replace(REX.tabCharWSP, '\t')
+ .replace(REX.commaGroup, ',')
+ .replace(REX.trimSpaces, '');
+
+ // parse, validate and split possible compound selectors
+ if ((expressions = parsed.match(reValidator)) && expressions.join('') === parsed) {
+ expressions = parsed.match(REX.splitGroup);
+ if (parsed[parsed.length - 1] === ',') {
+ emit(qsInvalid);
+ return Config.VERBOSITY ? undefined : false;
+ }
+ } else {
+ emit('\'' + selectors + '\'' + qsInvalid);
+ return Config.VERBOSITY ? undefined : false;
+ }
+
+ matchResolvers[selectors] = matchCollect(expressions, callback);
+
+ return matchAssert(matchResolvers[selectors].factory, element, callback);
+ };
+
+ // equivalent of w3c 'closest' method
+ const ancestor = function _closest(selectors, element, callback) {
+ if ((/:scope/i).test(selectors)) {
+ selectors = makeref(selectors, element);
+ }
+
+ while (element) {
+ if (match(selectors, element, callback)) break;
+ element = element.parentElement;
+ }
+ return element;
+ };
+
+ // equivalent of w3c 'querySelectorAll' method
+ const select = function _querySelectorAll(selectors, context, callback) {
+ let expressions; let nodes = []; let resolver;
+
+ context || (context = doc);
+
+ if (selectors) {
+ if ((resolver = selectResolvers[selectors])) {
+ if (resolver.context === context && resolver.callback === callback) {
+ const f = resolver.factory;
+ const h = resolver.htmlset;
+ const n = resolver.nodeset;
+ if (n.length > 1) {
+ const l = n.length;
+ for (let i = 0, l = n.length, list; l > i; ++i) {
+ list = compat[n[i][0]](context, n[i].slice(1))();
+ if (f[i] !== null) {
+ f[i](list, callback, context, nodes);
+ } else {
+ nodes = nodes.concat(list);
+ }
+ }
+ if (l > 1 && nodes.length > 1) {
+ nodes.sort(documentOrder);
+ hasDupes && (nodes = unique(nodes));
+ }
+ } else {
+ if (f[0]) {
+ nodes = f[0](h[0](), callback, context, nodes);
+ } else {
+ nodes = h[0]();
+ }
+ }
+ return typeof callback === 'function'
+ ? concatCall(nodes, callback)
+ : nodes;
+ }
+ }
+ }
+
+ lastSelected = selectors;
+
+ // arguments validation
+ if (arguments.length === 0) {
+ emit(qsNotArgs, 'TypeError');
+ return Config.VERBOSITY ? undefined : none;
+ } else if (arguments[0] === '') {
+ emit('\'\'' + qsInvalid);
+ return Config.VERBOSITY ? undefined : none;
+ } else if (lastContext !== context) {
+ lastContext = switchContext(context);
+ }
+
+ // input NULL or UNDEFINED
+ if (typeof selectors !== 'string') {
+ selectors = '' + selectors;
+ }
+
+ if ((/:scope/i).test(selectors)) {
+ selectors = makeref(selectors, context);
+ }
+
+ // normalize input string
+ const parsed = selectors
+ .replace(/\0|\\$/g, '\ufffd')
+ .replace(REX.combineWSP, '\x20')
+ .replace(REX.pseudosWSP, '$1')
+ .replace(REX.tabCharWSP, '\t')
+ .replace(REX.commaGroup, ',')
+ .replace(REX.trimSpaces, '');
+
+ // parse, validate and split possible compound selectors
+ if ((expressions = parsed.match(reValidator)) && expressions.join('') === parsed) {
+ expressions = parsed.match(REX.splitGroup);
+ if (parsed[parsed.length - 1] === ',') {
+ emit(qsInvalid);
+ return Config.VERBOSITY ? undefined : false;
+ }
+ } else {
+ emit('\'' + selectors + '\'' + qsInvalid);
+ return Config.VERBOSITY ? undefined : false;
+ }
+
+ // save/reuse factory and closure collection
+ selectResolvers[selectors] = collect(expressions, context, callback);
+
+ nodes = selectResolvers[selectors].results;
+
+ return typeof callback === 'function'
+ ? concatCall(nodes, callback)
+ : nodes;
+ };
+
+ // equivalent of w3c 'querySelector' method
+ const first = function _querySelector(selectors, context, callback) {
+ if (arguments.length === 0) {
+ emit(qsNotArgs, 'TypeError');
+ }
+ return select(selectors, context, typeof callback === 'function'
+ ? function firstMatch(element) {
+ callback(element);
+ return false;
+ }
+ : function firstMatch() {
+ return false;
+ }
+ )[0] || null;
+ };
+
+ // execute the engine initialization code
+ const initialize = function (d) {
+ setIdentifierSyntax();
+ lastContext = switchContext(d, true);
+ Snapshot.doc = doc;
+ Snapshot.from = doc;
+ Snapshot.byTag = byTag;
+ Snapshot.first = first;
+ Snapshot.match = match;
+ Snapshot.ancestor = ancestor;
+ Snapshot.nthOfType = nthOfType;
+ Snapshot.nthElement = nthElement;
+ Snapshot.hasAttributeNS = hasAttributeNS;
+ Snapshot.isTarget = isTarget;
+ Snapshot.isIndeterminate = isIndeterminate;
+ Snapshot.isContentEditable = isContentEditable;
+ };
+
+ initialize(doc);
+
+ // public exported methods/objects
+ const Dom = {
+ // exported engine methods
+ Version: version,
+ configure,
+ match,
+ closest: ancestor,
+ first,
+ select
+ };
+
+ return Dom;
+});