diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-14 14:46:37 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-14 14:46:37 -0800 |
| commit | afa87af01c79a9baa539f2992d32154d2a4739bd (patch) | |
| tree | 92c7416db734270a2fee1d72ee9cc119379ff8e1 /vanilla/node_modules/@asamuzakjp/nwsapi/src/nwsapi.js | |
| parent | 3b927e84d200402281f68181cd4253bc77e5528d (diff) | |
| download | neko-afa87af01c79a9baa539f2992d32154d2a4739bd.tar.gz neko-afa87af01c79a9baa539f2992d32154d2a4739bd.tar.bz2 neko-afa87af01c79a9baa539f2992d32154d2a4739bd.zip | |
task: delete vanilla js prototype\n\n- Removed vanilla/ directory and web/dist/vanilla directory\n- Updated Makefile, Dockerfile, and CI workflow to remove vanilla references\n- Cleaned up web/web.go to remove vanilla embed and routes\n- Verified build and tests pass\n\nCloses NK-2tcnmq
Diffstat (limited to 'vanilla/node_modules/@asamuzakjp/nwsapi/src/nwsapi.js')
| -rw-r--r-- | vanilla/node_modules/@asamuzakjp/nwsapi/src/nwsapi.js | 1855 |
1 files changed, 0 insertions, 1855 deletions
diff --git a/vanilla/node_modules/@asamuzakjp/nwsapi/src/nwsapi.js b/vanilla/node_modules/@asamuzakjp/nwsapi/src/nwsapi.js deleted file mode 100644 index e118fd5..0000000 --- a/vanilla/node_modules/@asamuzakjp/nwsapi/src/nwsapi.js +++ /dev/null @@ -1,1855 +0,0 @@ -/** - * 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; -}); |
