| 1 | /*
|
|---|
| 2 | * Copyright (C) 2007-2025 Diego Perini
|
|---|
| 3 | * All rights reserved.
|
|---|
| 4 | *
|
|---|
| 5 | * nwsapi.js - Fast CSS Selectors API Engine
|
|---|
| 6 | *
|
|---|
| 7 | * Author: Diego Perini <diego.perini at gmail com>
|
|---|
| 8 | * Version: 2.2.23
|
|---|
| 9 | * Created: 20070722
|
|---|
| 10 | * Release: 20251205
|
|---|
| 11 | *
|
|---|
| 12 | * License:
|
|---|
| 13 | * https://javascript.nwbox.com/nwsapi/MIT-LICENSE
|
|---|
| 14 | * Download:
|
|---|
| 15 | * https://javascript.nwbox.com/nwsapi/nwsapi.js
|
|---|
| 16 | */
|
|---|
| 17 |
|
|---|
| 18 | (function Export(global, factory) {
|
|---|
| 19 |
|
|---|
| 20 | 'use strict';
|
|---|
| 21 |
|
|---|
| 22 | if (typeof module == 'object' && typeof exports == 'object') {
|
|---|
| 23 | module.exports = factory;
|
|---|
| 24 | } else if (typeof define == 'function' && define['amd']) {
|
|---|
| 25 | define(factory);
|
|---|
| 26 | } else {
|
|---|
| 27 | global.NW || (global.NW = { });
|
|---|
| 28 | global.NW.Dom = factory(global, Export);
|
|---|
| 29 | }
|
|---|
| 30 |
|
|---|
| 31 | })(this, function Factory(global, Export) {
|
|---|
| 32 |
|
|---|
| 33 | var version = 'nwsapi-2.2.23',
|
|---|
| 34 |
|
|---|
| 35 | doc = global.document,
|
|---|
| 36 | root = doc.documentElement,
|
|---|
| 37 | slice = Array.prototype.slice,
|
|---|
| 38 |
|
|---|
| 39 | HSP = '[\\x20\\t]',
|
|---|
| 40 | VSP = '[\\r\\n\\f]',
|
|---|
| 41 | WSP = '[\\x20\\t\\r\\n\\f]',
|
|---|
| 42 |
|
|---|
| 43 | CFG = {
|
|---|
| 44 | // extensions
|
|---|
| 45 | operators: '[~*^$|]=|=',
|
|---|
| 46 | combinators: '[\\x20\\t>+~](?=[^>+~])'
|
|---|
| 47 | },
|
|---|
| 48 |
|
|---|
| 49 | HAS = {
|
|---|
| 50 | nestedself: ':has\\x28(?::has\\x28|.*)\\x29)\\x29',
|
|---|
| 51 | },
|
|---|
| 52 |
|
|---|
| 53 | NOT = {
|
|---|
| 54 | // not enclosed in double/single/parens/square
|
|---|
| 55 | double_enc: '(?=(?:[^"]*["][^"]*["])*[^"]*$)',
|
|---|
| 56 | single_enc: "(?=(?:[^']*['][^']*['])*[^']*$)",
|
|---|
| 57 | parens_enc: '(?![^\\x28]*\\x29)',
|
|---|
| 58 | square_enc: '(?![^\\x5b]*\\x5d)'
|
|---|
| 59 | },
|
|---|
| 60 |
|
|---|
| 61 | REX = {
|
|---|
| 62 | // regular expressions
|
|---|
| 63 | HasEscapes: RegExp('\\\\'),
|
|---|
| 64 | HexNumbers: RegExp('^[0-9a-fA-F]'),
|
|---|
| 65 | EscOrQuote: RegExp('^\\\\|[\\x22\\x27]'),
|
|---|
| 66 | RegExpChar: RegExp('(?!\\\\)[\\\\^$.,*+?()[\\]{}|\\/]', 'g'),
|
|---|
| 67 | TrimSpaces: RegExp('^' + WSP + '+|' + WSP + '+$|' + VSP, 'g'),
|
|---|
| 68 | SplitGroup: RegExp('(\\([^)]*\\)|\\[[^[]*\\]|\\\\.|[^,])+', 'g'),
|
|---|
| 69 | CommaGroup: RegExp('(\\s*,\\s*)' + NOT.square_enc + NOT.parens_enc, 'g'),
|
|---|
| 70 | FixEscapes: RegExp('\\\\([0-9a-fA-F]{1,6}' + WSP + '?|.)|([\\x22\\x27])', 'g'),
|
|---|
| 71 | CombineWSP: RegExp('[\\n\\r\\f\\x20]+' + NOT.single_enc + NOT.double_enc, 'g'),
|
|---|
| 72 | TabCharWSP: RegExp('(\\x20?\\t+\\x20?)' + NOT.single_enc + NOT.double_enc, 'g'),
|
|---|
| 73 | PseudosWSP: RegExp('\\s+([-+])\\s+' + NOT.square_enc, 'g')
|
|---|
| 74 | },
|
|---|
| 75 |
|
|---|
| 76 | STD = {
|
|---|
| 77 | combinator: RegExp('\\s?([>+~])\\s?', 'g'),
|
|---|
| 78 | apimethods: RegExp('^(?:\\w+|\\*)\\|'),
|
|---|
| 79 | namespaces: RegExp('(\\*|\\w+)\\|[\\w-]+')
|
|---|
| 80 | },
|
|---|
| 81 |
|
|---|
| 82 | GROUPS = {
|
|---|
| 83 | // pseudo-classes requiring parameters
|
|---|
| 84 | linguistic: '(dir|lang)(?:\\x28\\s?([-\\w]{2,})\\s?\\x29)',
|
|---|
| 85 | logicalsel: '(is|where|matches|not|has)(?:\\x28\\s?(' + '[^()]*|.*' + ')\\s?\\x29)',
|
|---|
| 86 | treestruct: '(nth(?:-last)?(?:-child|-of\\-type))(?:\\x28\\s?(even|odd|(?:[-+]?\\d*)(?:n\\s?[-+]?\\s?\\d*)?)\\s?\\x29)',
|
|---|
| 87 | // pseudo-classes not requiring parameters
|
|---|
| 88 | locationpc: '(any\\-link|link|visited|target|defined)\\b',
|
|---|
| 89 | useraction: '(hover|active|focus\\-within|focus\\-visible|focus)\\b',
|
|---|
| 90 | structural: '(scope|root|empty|(?:(?:first|last|only)(?:-child|\\-of\\-type)))\\b',
|
|---|
| 91 | inputstate: '(enabled|disabled|read\\-only|read\\-write|placeholder\\-shown|default)\\b',
|
|---|
| 92 | inputvalue: '(checked|indeterminate|required|optional|valid|invalid|in\\-range|out\\-of\\-range)\\b',
|
|---|
| 93 | // pseudo-classes not requiring parameters and describing functional state
|
|---|
| 94 | rsrc_state: '(playing|paused|seeking|buffering|stalled|muted|volume-locked)\\b',
|
|---|
| 95 | disp_state: '(open|closed|modal|fullscreen|picture-in-picture)\\b',
|
|---|
| 96 | time_state: '(current|past|future)\\b',
|
|---|
| 97 | // pseudo-classes for parsing only selectors
|
|---|
| 98 | pseudo_nop: '(autofill|-webkit\\-autofill)\\b',
|
|---|
| 99 | // pseudo-elements starting with single colon (:)
|
|---|
| 100 | pseudo_sng: '(after|before|first\\-letter|first\\-line)\\b',
|
|---|
| 101 | // pseudo-elements starting with double colon (::)
|
|---|
| 102 | pseudo_dbl: ':(after|before|first\\-letter|first\\-line|selection|placeholder|-webkit-[-a-zA-Z0-9]{2,})\\b'
|
|---|
| 103 | },
|
|---|
| 104 |
|
|---|
| 105 | Patterns = {
|
|---|
| 106 | // pseudo-classes
|
|---|
| 107 | treestruct: RegExp('^:(?:' + GROUPS.treestruct + ')(.*)', 'i'),
|
|---|
| 108 | structural: RegExp('^:(?:' + GROUPS.structural + ')(.*)', 'i'),
|
|---|
| 109 | linguistic: RegExp('^:(?:' + GROUPS.linguistic + ')(.*)', 'i'),
|
|---|
| 110 | useraction: RegExp('^:(?:' + GROUPS.useraction + ')(.*)', 'i'),
|
|---|
| 111 | inputstate: RegExp('^:(?:' + GROUPS.inputstate + ')(.*)', 'i'),
|
|---|
| 112 | inputvalue: RegExp('^:(?:' + GROUPS.inputvalue + ')(.*)', 'i'),
|
|---|
| 113 | rsrc_state: RegExp('^:(?:' + GROUPS.rsrc_state + ')(.*)', 'i'),
|
|---|
| 114 | disp_state: RegExp('^:(?:' + GROUPS.disp_state + ')(.*)', 'i'),
|
|---|
| 115 | time_state: RegExp('^:(?:' + GROUPS.time_state + ')(.*)', 'i'),
|
|---|
| 116 | locationpc: RegExp('^:(?:' + GROUPS.locationpc + ')(.*)', 'i'),
|
|---|
| 117 | logicalsel: RegExp('^:(?:' + GROUPS.logicalsel + ')(.*)', 'i'),
|
|---|
| 118 | pseudo_nop: RegExp('^:(?:' + GROUPS.pseudo_nop + ')(.*)', 'i'),
|
|---|
| 119 | pseudo_sng: RegExp('^:(?:' + GROUPS.pseudo_sng + ')(.*)', 'i'),
|
|---|
| 120 | pseudo_dbl: RegExp('^:(?:' + GROUPS.pseudo_dbl + ')(.*)', 'i'),
|
|---|
| 121 | // combinator symbols
|
|---|
| 122 | children: RegExp('^' + WSP + '?\\>' + WSP + '?(.*)'),
|
|---|
| 123 | adjacent: RegExp('^' + WSP + '?\\+' + WSP + '?(.*)'),
|
|---|
| 124 | relative: RegExp('^' + WSP + '?\\~' + WSP + '?(.*)'),
|
|---|
| 125 | ancestor: RegExp('^' + WSP + '+(.*)'),
|
|---|
| 126 | // universal & namespace
|
|---|
| 127 | universal: RegExp('^(\\*)(.*)'),
|
|---|
| 128 | namespace: RegExp('^(\\*|[\\w-]+)?\\|(.*)')
|
|---|
| 129 | },
|
|---|
| 130 |
|
|---|
| 131 | // regexp to better aproximate detection of RTL languages (Arabic)
|
|---|
| 132 | RTL = RegExp('^(?:[\\u0627-\\u064a]|[\\u0591-\\u08ff]|[\\ufb1d-\\ufdfd]|[\\ufe70-\\ufefc])+$'),
|
|---|
| 133 |
|
|---|
| 134 | // emulate firefox error strings
|
|---|
| 135 | qsNotArgs = 'Not enough arguments',
|
|---|
| 136 | qsInvalid = ' is not a valid selector',
|
|---|
| 137 |
|
|---|
| 138 | // detect structural pseudo-classes in selectors
|
|---|
| 139 | reNthElem = RegExp('(:nth(?:-last)?-child)', 'i'),
|
|---|
| 140 | reNthType = RegExp('(:nth(?:-last)?-of-type)', 'i'),
|
|---|
| 141 |
|
|---|
| 142 | // placeholder for global regexp
|
|---|
| 143 | reOptimizer,
|
|---|
| 144 | reValidator,
|
|---|
| 145 |
|
|---|
| 146 | // special handling configuration flags
|
|---|
| 147 | Config = {
|
|---|
| 148 | IDS_DUPES: true,
|
|---|
| 149 | FORGIVING: true,
|
|---|
| 150 | NODE_LIST: false,
|
|---|
| 151 | LOGERRORS: true,
|
|---|
| 152 | USR_EVENT: true,
|
|---|
| 153 | VERBOSITY: true
|
|---|
| 154 | },
|
|---|
| 155 |
|
|---|
| 156 | NAMESPACE,
|
|---|
| 157 | QUIRKS_MODE,
|
|---|
| 158 | HTML_DOCUMENT,
|
|---|
| 159 |
|
|---|
| 160 | ATTR_STD_OPS = {
|
|---|
| 161 | '=': 1, '^=': 1, '$=': 1, '|=': 1, '*=': 1, '~=': 1
|
|---|
| 162 | },
|
|---|
| 163 |
|
|---|
| 164 | HTML_TABLE = {
|
|---|
| 165 | 'accept': 1, 'accept-charset': 1, 'align': 1, 'alink': 1, 'axis': 1,
|
|---|
| 166 | 'bgcolor': 1, 'charset': 1, 'checked': 1, 'clear': 1, 'codetype': 1, 'color': 1,
|
|---|
| 167 | 'compact': 1, 'declare': 1, 'defer': 1, 'dir': 1, 'direction': 1, 'disabled': 1,
|
|---|
| 168 | 'enctype': 1, 'face': 1, 'frame': 1, 'hreflang': 1, 'http-equiv': 1, 'lang': 1,
|
|---|
| 169 | 'language': 1, 'link': 1, 'media': 1, 'method': 1, 'multiple': 1, 'nohref': 1,
|
|---|
| 170 | 'noresize': 1, 'noshade': 1, 'nowrap': 1, 'readonly': 1, 'rel': 1, 'rev': 1,
|
|---|
| 171 | 'rules': 1, 'scope': 1, 'scrolling': 1, 'selected': 1, 'shape': 1, 'target': 1,
|
|---|
| 172 | 'text': 1, 'type': 1, 'valign': 1, 'valuetype': 1, 'vlink': 1
|
|---|
| 173 | },
|
|---|
| 174 |
|
|---|
| 175 | Combinators = { },
|
|---|
| 176 |
|
|---|
| 177 | Selectors = { },
|
|---|
| 178 |
|
|---|
| 179 | Operators = {
|
|---|
| 180 | '=': { p1: '^',
|
|---|
| 181 | p2: '$',
|
|---|
| 182 | p3: 'true' },
|
|---|
| 183 | '^=': { p1: '^',
|
|---|
| 184 | p2: '',
|
|---|
| 185 | p3: 'true' },
|
|---|
| 186 | '$=': { p1: '',
|
|---|
| 187 | p2: '$',
|
|---|
| 188 | p3: 'true' },
|
|---|
| 189 | '*=': { p1: '',
|
|---|
| 190 | p2: '',
|
|---|
| 191 | p3: 'true' },
|
|---|
| 192 | '|=': { p1: '^',
|
|---|
| 193 | p2: '(-|$)',
|
|---|
| 194 | p3: 'true' },
|
|---|
| 195 | '~=': { p1: '(^|\\s)',
|
|---|
| 196 | p2: '(\\s|$)',
|
|---|
| 197 | p3: 'true' }
|
|---|
| 198 | },
|
|---|
| 199 |
|
|---|
| 200 | concatCall =
|
|---|
| 201 | function(nodes, callback) {
|
|---|
| 202 | var i = 0, l = nodes.length, list = Array(l);
|
|---|
| 203 | while (l > i) {
|
|---|
| 204 | if (false === callback(list[i] = nodes[i])) break;
|
|---|
| 205 | ++i;
|
|---|
| 206 | }
|
|---|
| 207 | return list;
|
|---|
| 208 | },
|
|---|
| 209 |
|
|---|
| 210 | concatList =
|
|---|
| 211 | function(list, nodes) {
|
|---|
| 212 | var i = -1, l = nodes.length;
|
|---|
| 213 | while (l--) { list[list.length] = nodes[++i]; }
|
|---|
| 214 | return list;
|
|---|
| 215 | },
|
|---|
| 216 |
|
|---|
| 217 | // only define the toNodeList helper if explicitly enabled in Config,
|
|---|
| 218 | // a safety measure for headless hosts missing feature/implementation
|
|---|
| 219 | toNodeList =
|
|---|
| 220 | Config.NODE_LIST == false ?
|
|---|
| 221 | function(x) { return x; } :
|
|---|
| 222 | function() {
|
|---|
| 223 | // create a DocumentFragment
|
|---|
| 224 | var emptyNL = doc.createDocumentFragment().childNodes;
|
|---|
| 225 |
|
|---|
| 226 | // this is returned from a self-executing function so that
|
|---|
| 227 | // the DocumentFragment isn't repeatedly created
|
|---|
| 228 | return function(nodeArray) {
|
|---|
| 229 | // check if it is already a nodelist
|
|---|
| 230 | if (isInstanceOf(nodeArray)) return nodeArray;
|
|---|
| 231 |
|
|---|
| 232 | // if it's a single element, wrap it in a classic array
|
|---|
| 233 | if (!Array.isArray(nodeArray)) nodeArray = [nodeArray];
|
|---|
| 234 |
|
|---|
| 235 | // base an object on emptyNL
|
|---|
| 236 | var fakeNL = Object.create(emptyNL, {
|
|---|
| 237 | 'length': {
|
|---|
| 238 | value: nodeArray.length, enumerable: false
|
|---|
| 239 | },
|
|---|
| 240 | 'item': {
|
|---|
| 241 | 'value': function(i) {
|
|---|
| 242 | return this[+i || 0];
|
|---|
| 243 | },
|
|---|
| 244 | enumerable: false
|
|---|
| 245 | }
|
|---|
| 246 | });
|
|---|
| 247 |
|
|---|
| 248 | // copy the array elemnts
|
|---|
| 249 | nodeArray.forEach(function (v, i) { fakeNL[i] = v; });
|
|---|
| 250 |
|
|---|
| 251 | // return an object pretending to be a NodeList.
|
|---|
| 252 | return fakeNL;
|
|---|
| 253 | };
|
|---|
| 254 | }(),
|
|---|
| 255 |
|
|---|
| 256 | isInstanceOf =
|
|---|
| 257 | function(nodes) {
|
|---|
| 258 | return nodes instanceof global.NodeList;
|
|---|
| 259 | },
|
|---|
| 260 |
|
|---|
| 261 | documentOrder =
|
|---|
| 262 | function(a, b) {
|
|---|
| 263 | if (!hasDupes && a === b) {
|
|---|
| 264 | hasDupes = true;
|
|---|
| 265 | return 0;
|
|---|
| 266 | }
|
|---|
| 267 | return a.compareDocumentPosition(b) & 4 ? -1 : 1;
|
|---|
| 268 | },
|
|---|
| 269 |
|
|---|
| 270 | hasDupes = false,
|
|---|
| 271 |
|
|---|
| 272 | unique =
|
|---|
| 273 | function(nodes) {
|
|---|
| 274 | var i = 0, j = -1, l = nodes.length + 1, list = [ ];
|
|---|
| 275 | while (--l) {
|
|---|
| 276 | if (nodes[i++] === nodes[i]) continue;
|
|---|
| 277 | list[++j] = nodes[i - 1];
|
|---|
| 278 | }
|
|---|
| 279 | hasDupes = false;
|
|---|
| 280 | return list;
|
|---|
| 281 | },
|
|---|
| 282 |
|
|---|
| 283 | switchContext =
|
|---|
| 284 | function(context, force) {
|
|---|
| 285 | var oldDoc = doc;
|
|---|
| 286 | doc = context.ownerDocument || context;
|
|---|
| 287 | if (force || oldDoc !== doc) {
|
|---|
| 288 | // force a new check for each document change
|
|---|
| 289 | // performed before the next select operation
|
|---|
| 290 | root = doc.documentElement;
|
|---|
| 291 | HTML_DOCUMENT = isHTML(doc);
|
|---|
| 292 | QUIRKS_MODE = HTML_DOCUMENT &&
|
|---|
| 293 | doc.compatMode.indexOf('CSS') < 0;
|
|---|
| 294 | NAMESPACE = root && root.namespaceURI;
|
|---|
| 295 | Snapshot.doc = doc;
|
|---|
| 296 | Snapshot.root = root;
|
|---|
| 297 | }
|
|---|
| 298 | return (Snapshot.from = context);
|
|---|
| 299 | },
|
|---|
| 300 |
|
|---|
| 301 | // convert single codepoint to UTF-16 encoding
|
|---|
| 302 | codePointToUTF16 =
|
|---|
| 303 | function(codePoint) {
|
|---|
| 304 | // out of range, use replacement character
|
|---|
| 305 | if (codePoint < 1 || codePoint > 0x10ffff ||
|
|---|
| 306 | (codePoint > 0xd7ff && codePoint < 0xe000)) {
|
|---|
| 307 | return '\\ufffd';
|
|---|
| 308 | }
|
|---|
| 309 | // javascript strings are UTF-16 encoded
|
|---|
| 310 | if (codePoint < 0x10000) {
|
|---|
| 311 | var lowHex = '000' + codePoint.toString(16);
|
|---|
| 312 | return '\\u' + lowHex.substr(lowHex.length - 4);
|
|---|
| 313 | }
|
|---|
| 314 | // supplementary high + low surrogates
|
|---|
| 315 | return '\\u' + (((codePoint - 0x10000) >> 0x0a) + 0xd800).toString(16) +
|
|---|
| 316 | '\\u' + (((codePoint - 0x10000) % 0x400) + 0xdc00).toString(16);
|
|---|
| 317 | },
|
|---|
| 318 |
|
|---|
| 319 | // convert single codepoint to string
|
|---|
| 320 | stringFromCodePoint =
|
|---|
| 321 | function(codePoint) {
|
|---|
| 322 | // out of range, use replacement character
|
|---|
| 323 | if (codePoint < 1 || codePoint > 0x10ffff ||
|
|---|
| 324 | (codePoint > 0xd7ff && codePoint < 0xe000)) {
|
|---|
| 325 | return '\ufffd';
|
|---|
| 326 | }
|
|---|
| 327 | if (codePoint < 0x10000) {
|
|---|
| 328 | return String.fromCharCode(codePoint);
|
|---|
| 329 | }
|
|---|
| 330 | return String.fromCodePoint ?
|
|---|
| 331 | String.fromCodePoint(codePoint) :
|
|---|
| 332 | String.fromCharCode(
|
|---|
| 333 | ((codePoint - 0x10000) >> 0x0a) + 0xd800,
|
|---|
| 334 | ((codePoint - 0x10000) % 0x400) + 0xdc00);
|
|---|
| 335 | },
|
|---|
| 336 |
|
|---|
| 337 | // convert escape sequence in a CSS string or identifier
|
|---|
| 338 | // to javascript string with javascript escape sequences
|
|---|
| 339 | convertEscapes =
|
|---|
| 340 | function(str) {
|
|---|
| 341 | return REX.HasEscapes.test(str) ?
|
|---|
| 342 | str.replace(REX.FixEscapes,
|
|---|
| 343 | function(substring, p1, p2) {
|
|---|
| 344 | // unescaped " or '
|
|---|
| 345 | return p2 ? '\\' + p2 :
|
|---|
| 346 | // javascript strings are UTF-16 encoded
|
|---|
| 347 | REX.HexNumbers.test(p1) ? codePointToUTF16(parseInt(p1, 16)) :
|
|---|
| 348 | // \' \"
|
|---|
| 349 | REX.EscOrQuote.test(p1) ? substring :
|
|---|
| 350 | // \g \h \. \# etc
|
|---|
| 351 | p1;
|
|---|
| 352 | }
|
|---|
| 353 | ) : str;
|
|---|
| 354 | },
|
|---|
| 355 |
|
|---|
| 356 | // convert escape sequence in a CSS string or identifier
|
|---|
| 357 | // to javascript string with characters representations
|
|---|
| 358 | unescapeIdentifier =
|
|---|
| 359 | function(str) {
|
|---|
| 360 | return REX.HasEscapes.test(str) ?
|
|---|
| 361 | str.replace(REX.FixEscapes,
|
|---|
| 362 | function(substring, p1, p2) {
|
|---|
| 363 | // unescaped " or '
|
|---|
| 364 | return p2 ? p2 :
|
|---|
| 365 | // javascript strings are UTF-16 encoded
|
|---|
| 366 | REX.HexNumbers.test(p1) ? stringFromCodePoint(parseInt(p1, 16)) :
|
|---|
| 367 | // \' \"
|
|---|
| 368 | REX.EscOrQuote.test(p1) ? substring :
|
|---|
| 369 | // \g \h \. \# etc
|
|---|
| 370 | p1;
|
|---|
| 371 | }
|
|---|
| 372 | ) : str;
|
|---|
| 373 | },
|
|---|
| 374 |
|
|---|
| 375 | method = {
|
|---|
| 376 | '#': 'getElementById',
|
|---|
| 377 | '*': 'getElementsByTagName',
|
|---|
| 378 | '|': 'getElementsByTagNameNS',
|
|---|
| 379 | '.': 'getElementsByClassName'
|
|---|
| 380 | },
|
|---|
| 381 |
|
|---|
| 382 | compat = {
|
|---|
| 383 | '#': (c, n) => (e, f) => byId(n, c),
|
|---|
| 384 | '*': (c, n) => (e, f) => byTag(n, c),
|
|---|
| 385 | '|': (c, n) => (e, f) => byTagNS(n, c),
|
|---|
| 386 | '.': (c, n) => (e, f) => byClass(n, c),
|
|---|
| 387 | },
|
|---|
| 388 |
|
|---|
| 389 | // find duplicate ids using iterative walk
|
|---|
| 390 | byIdRaw =
|
|---|
| 391 | function(id, context) {
|
|---|
| 392 | var node = context, nodes = [ ], next = node.firstElementChild;
|
|---|
| 393 | while ((node = next)) {
|
|---|
| 394 | node.id == id && (nodes[nodes.length] = node);
|
|---|
| 395 | if ((next = node.firstElementChild || node.nextElementSibling)) continue;
|
|---|
| 396 | while (!next && (node = node.parentElement) && node !== context) {
|
|---|
| 397 | next = node.nextElementSibling;
|
|---|
| 398 | }
|
|---|
| 399 | }
|
|---|
| 400 | return nodes;
|
|---|
| 401 | },
|
|---|
| 402 |
|
|---|
| 403 | // context agnostic getElementById
|
|---|
| 404 | byId =
|
|---|
| 405 | function(id, context) {
|
|---|
| 406 | var e, i, l, nodes, api = method['#'];
|
|---|
| 407 |
|
|---|
| 408 | // duplicates id allowed
|
|---|
| 409 | if (Config.IDS_DUPES === false) {
|
|---|
| 410 | if (api in context) {
|
|---|
| 411 | return (e = context[api](id)) ? [ e ] : none;
|
|---|
| 412 | }
|
|---|
| 413 | } else {
|
|---|
| 414 | if ('all' in context) {
|
|---|
| 415 | if ((e = context.all[id])) {
|
|---|
| 416 | if (e.nodeType == 1) return e.getAttribute('id') != id ? [ ] : [ e ];
|
|---|
| 417 | else if (id == 'length') return (e = context[api](id)) ? [ e ] : none;
|
|---|
| 418 | for (i = 0, l = e.length, nodes = [ ]; l > i; ++i) {
|
|---|
| 419 | if (e[i].id == id) nodes[nodes.length] = e[i];
|
|---|
| 420 | }
|
|---|
| 421 | return nodes && nodes.length ? nodes : [ nodes ];
|
|---|
| 422 | } else return none;
|
|---|
| 423 | }
|
|---|
| 424 | }
|
|---|
| 425 |
|
|---|
| 426 | return byIdRaw(id, context);
|
|---|
| 427 | },
|
|---|
| 428 |
|
|---|
| 429 | // wrapped up namespaced TagName api calls
|
|---|
| 430 | byTagNS =
|
|---|
| 431 | function(context, tag) {
|
|---|
| 432 | return byTag(tag, context);
|
|---|
| 433 | },
|
|---|
| 434 |
|
|---|
| 435 | // context agnostic getElementsByTagName
|
|---|
| 436 | byTag =
|
|---|
| 437 | function(tag, context) {
|
|---|
| 438 | var e, nodes, api = method['*'];
|
|---|
| 439 | // DOCUMENT_NODE (9) & ELEMENT_NODE (1)
|
|---|
| 440 | if (api in context) {
|
|---|
| 441 | return slice.call(context[api](tag));
|
|---|
| 442 | } else {
|
|---|
| 443 | tag = tag.toLowerCase();
|
|---|
| 444 | // DOCUMENT_FRAGMENT_NODE (11)
|
|---|
| 445 | if ((e = context.firstElementChild)) {
|
|---|
| 446 | if (!(e.nextElementSibling || tag == '*' || e.localName == tag)) {
|
|---|
| 447 | return slice.call(e[api](tag));
|
|---|
| 448 | } else {
|
|---|
| 449 | nodes = [ ];
|
|---|
| 450 | do {
|
|---|
| 451 | if (tag == '*' || e.localName == tag) nodes[nodes.length] = e;
|
|---|
| 452 | concatList(nodes, e[api](tag));
|
|---|
| 453 | } while ((e = e.nextElementSibling));
|
|---|
| 454 | }
|
|---|
| 455 | } else nodes = none;
|
|---|
| 456 | }
|
|---|
| 457 | return !Config.NODE_LIST ?
|
|---|
| 458 | nodes : isInstanceOf(nodes) ?
|
|---|
| 459 | nodes : toNodeList(nodes);
|
|---|
| 460 | },
|
|---|
| 461 |
|
|---|
| 462 | // context agnostic getElementsByClassName
|
|---|
| 463 | byClass =
|
|---|
| 464 | function(cls, context) {
|
|---|
| 465 | var e, nodes, api = method['.'], reCls;
|
|---|
| 466 | // DOCUMENT_NODE (9) & ELEMENT_NODE (1)
|
|---|
| 467 | if (api in context) {
|
|---|
| 468 | return slice.call(context[api](cls));
|
|---|
| 469 | } else {
|
|---|
| 470 | // DOCUMENT_FRAGMENT_NODE (11)
|
|---|
| 471 | if ((e = context.firstElementChild)) {
|
|---|
| 472 | reCls = RegExp('(^|\\s)' + cls + '(\\s|$)', QUIRKS_MODE ? 'i' : '');
|
|---|
| 473 | if (!(e.nextElementSibling || reCls.test(e.className))) {
|
|---|
| 474 | return slice.call(e[api](cls));
|
|---|
| 475 | } else {
|
|---|
| 476 | nodes = [ ];
|
|---|
| 477 | do {
|
|---|
| 478 | if (reCls.test(e.className)) nodes[nodes.length] = e;
|
|---|
| 479 | concatList(nodes, e[api](cls));
|
|---|
| 480 | } while ((e = e.nextElementSibling));
|
|---|
| 481 | }
|
|---|
| 482 | } else nodes = none;
|
|---|
| 483 | }
|
|---|
| 484 | return !Config.NODE_LIST ?
|
|---|
| 485 | nodes : isInstanceof(nodes) ?
|
|---|
| 486 | nodes : toNodeList(nodes);
|
|---|
| 487 | },
|
|---|
| 488 |
|
|---|
| 489 | // namespace aware hasAttribute
|
|---|
| 490 | // helper for XML/XHTML documents
|
|---|
| 491 | hasAttributeNS =
|
|---|
| 492 | function(e, name) {
|
|---|
| 493 | var i, l, attr = e.getAttributeNames();
|
|---|
| 494 | name = RegExp(':?' + name + '$', HTML_DOCUMENT ? 'i' : '');
|
|---|
| 495 | for (i = 0, l = attr.length; l > i; ++i) {
|
|---|
| 496 | if (name.test(attr[i])) return true;
|
|---|
| 497 | }
|
|---|
| 498 | return false;
|
|---|
| 499 | },
|
|---|
| 500 |
|
|---|
| 501 | // fast resolver for the :nth-child() and :nth-last-child() pseudo-classes
|
|---|
| 502 | nthElement = (function() {
|
|---|
| 503 | var idx = 0, len = 0, set = 0, parent = undefined, parents = Array(), nodes = Array();
|
|---|
| 504 | return function(element, dir) {
|
|---|
| 505 | // ensure caches are emptied after each run, invoking with dir = 2
|
|---|
| 506 | if (dir == 2) {
|
|---|
| 507 | idx = 0; len = 0; set = 0; nodes.length = 0;
|
|---|
| 508 | parents.length = 0; parent = undefined;
|
|---|
| 509 | return -1;
|
|---|
| 510 | }
|
|---|
| 511 | var e, i, j, k, l;
|
|---|
| 512 | if (parent === element.parentElement) {
|
|---|
| 513 | i = set; j = idx; l = len;
|
|---|
| 514 | } else {
|
|---|
| 515 | l = parents.length;
|
|---|
| 516 | parent = element.parentElement;
|
|---|
| 517 | for (i = -1, j = 0, k = l - 1; l > j; ++j, --k) {
|
|---|
| 518 | if (parents[j] === parent) { i = j; break; }
|
|---|
| 519 | if (parents[k] === parent) { i = k; break; }
|
|---|
| 520 | }
|
|---|
| 521 | if (i < 0) {
|
|---|
| 522 | parents[i = l] = parent;
|
|---|
| 523 | l = 0; nodes[i] = Array();
|
|---|
| 524 | e = parent && parent.firstElementChild || element;
|
|---|
| 525 | while (e) { nodes[i][l] = e; if (e === element) j = l; e = e.nextElementSibling; ++l; }
|
|---|
| 526 | set = i; idx = 0; len = l;
|
|---|
| 527 | if (l < 2) return l;
|
|---|
| 528 | } else {
|
|---|
| 529 | l = nodes[i].length;
|
|---|
| 530 | set = i;
|
|---|
| 531 | }
|
|---|
| 532 | }
|
|---|
| 533 | if (element !== nodes[i][j] && element !== nodes[i][j = 0]) {
|
|---|
| 534 | for (j = 0, e = nodes[i], k = l - 1; l > j; ++j, --k) {
|
|---|
| 535 | if (e[j] === element) { break; }
|
|---|
| 536 | if (e[k] === element) { j = k; break; }
|
|---|
| 537 | }
|
|---|
| 538 | }
|
|---|
| 539 | idx = j + 1; len = l;
|
|---|
| 540 | return dir ? l - j : idx;
|
|---|
| 541 | };
|
|---|
| 542 | })(),
|
|---|
| 543 |
|
|---|
| 544 | // fast resolver for the :nth-of-type() and :nth-last-of-type() pseudo-classes
|
|---|
| 545 | nthOfType = (function() {
|
|---|
| 546 | var idx = 0, len = 0, set = 0, parent = undefined, parents = Array(), nodes = Array();
|
|---|
| 547 | return function(element, dir) {
|
|---|
| 548 | // ensure caches are emptied after each run, invoking with dir = 2
|
|---|
| 549 | if (dir == 2) {
|
|---|
| 550 | idx = 0; len = 0; set = 0; nodes.length = 0;
|
|---|
| 551 | parents.length = 0; parent = undefined;
|
|---|
| 552 | return -1;
|
|---|
| 553 | }
|
|---|
| 554 | var e, i, j, k, l, name = element.localName;
|
|---|
| 555 | if (nodes[set] && nodes[set][name] && parent === element.parentElement) {
|
|---|
| 556 | i = set; j = idx; l = len;
|
|---|
| 557 | } else {
|
|---|
| 558 | l = parents.length;
|
|---|
| 559 | parent = element.parentElement;
|
|---|
| 560 | for (i = -1, j = 0, k = l - 1; l > j; ++j, --k) {
|
|---|
| 561 | if (parents[j] === parent) { i = j; break; }
|
|---|
| 562 | if (parents[k] === parent) { i = k; break; }
|
|---|
| 563 | }
|
|---|
| 564 | if (i < 0 || !nodes[i][name]) {
|
|---|
| 565 | parents[i = l] = parent;
|
|---|
| 566 | nodes[i] || (nodes[i] = Object());
|
|---|
| 567 | l = 0; nodes[i][name] = Array();
|
|---|
| 568 | e = parent && parent.firstElementChild || element;
|
|---|
| 569 | while (e) { if (e === element) j = l; if (e.localName == name) { nodes[i][name][l] = e; ++l; } e = e.nextElementSibling; }
|
|---|
| 570 | set = i; idx = j; len = l;
|
|---|
| 571 | if (l < 2) return l;
|
|---|
| 572 | } else {
|
|---|
| 573 | l = nodes[i][name].length;
|
|---|
| 574 | set = i;
|
|---|
| 575 | }
|
|---|
| 576 | }
|
|---|
| 577 | if (element !== nodes[i][name][j] && element !== nodes[i][name][j = 0]) {
|
|---|
| 578 | for (j = 0, e = nodes[i][name], k = l - 1; l > j; ++j, --k) {
|
|---|
| 579 | if (e[j] === element) { break; }
|
|---|
| 580 | if (e[k] === element) { j = k; break; }
|
|---|
| 581 | }
|
|---|
| 582 | }
|
|---|
| 583 | idx = j + 1; len = l;
|
|---|
| 584 | return dir ? l - j : idx;
|
|---|
| 585 | };
|
|---|
| 586 | })(),
|
|---|
| 587 |
|
|---|
| 588 | // check if the document type is HTML
|
|---|
| 589 | isHTML =
|
|---|
| 590 | function(node) {
|
|---|
| 591 | var doc = node.ownerDocument || node;
|
|---|
| 592 | return doc.nodeType == 9 &&
|
|---|
| 593 | // contentType not in IE <= 11
|
|---|
| 594 | 'contentType' in doc ?
|
|---|
| 595 | doc.contentType.indexOf('/html') > 0 :
|
|---|
| 596 | doc.createElement('DiV').localName == 'div';
|
|---|
| 597 | },
|
|---|
| 598 |
|
|---|
| 599 | // return node if node is focusable
|
|---|
| 600 | // or false if node isn't focusable
|
|---|
| 601 | isFocusable =
|
|---|
| 602 | function(node) {
|
|---|
| 603 | var doc = node.ownerDocument;
|
|---|
| 604 | if (node.contentDocument&&node.localName== 'iframe') { return false; }
|
|---|
| 605 | if (doc.hasFocus() && node === doc.activeElement) {
|
|---|
| 606 | if (node.type || node.href || typeof node.tabIndex == 'number') {
|
|---|
| 607 | return node;
|
|---|
| 608 | }
|
|---|
| 609 | }
|
|---|
| 610 | return false;
|
|---|
| 611 | },
|
|---|
| 612 |
|
|---|
| 613 | // check if node content is editable
|
|---|
| 614 | isContentEditable =
|
|---|
| 615 | function(node) {
|
|---|
| 616 | var attrValue = 'inherit';
|
|---|
| 617 | if (node.hasAttribute('contenteditable')) {
|
|---|
| 618 | attrValue = node.getAttribute('contenteditable');
|
|---|
| 619 | }
|
|---|
| 620 | switch (attrValue) {
|
|---|
| 621 | case '':
|
|---|
| 622 | case 'plaintext-only':
|
|---|
| 623 | case 'true':
|
|---|
| 624 | return true;
|
|---|
| 625 | case 'false':
|
|---|
| 626 | return false;
|
|---|
| 627 | default:
|
|---|
| 628 | if (node.parentNode && node.parentNode.nodeType === 1) {
|
|---|
| 629 | return isContentEditable(node.parentNode);
|
|---|
| 630 | }
|
|---|
| 631 | return false;
|
|---|
| 632 | }
|
|---|
| 633 | },
|
|---|
| 634 |
|
|---|
| 635 | // check media resources is playing
|
|---|
| 636 | isPlaying =
|
|---|
| 637 | function(media) {
|
|---|
| 638 | // for <audio>, <video>, <source> and <track> elements
|
|---|
| 639 | var parent = media instanceof HTMLMediaElement ? null : media.parentElement;
|
|---|
| 640 | return (
|
|---|
| 641 | !!( media && media.currentTime > 0 && !media.paused && !media.ended && media.readyState > 2) ||
|
|---|
| 642 | !!(parent && parent.currentTime > 0 && !parent.paused && !parent.ended && parent.readyState > 2));
|
|---|
| 643 | },
|
|---|
| 644 |
|
|---|
| 645 | // configure the engine to use special handling
|
|---|
| 646 | configure =
|
|---|
| 647 | function(option, clear) {
|
|---|
| 648 | if (typeof option == 'string') { return !!Config[option]; }
|
|---|
| 649 | if (typeof option != 'object') { return Config; }
|
|---|
| 650 | for (var i in option) {
|
|---|
| 651 | Config[i] = !!option[i];
|
|---|
| 652 | }
|
|---|
| 653 | // clear lambda cache
|
|---|
| 654 | if (clear) {
|
|---|
| 655 | matchResolvers = { };
|
|---|
| 656 | selectResolvers = { };
|
|---|
| 657 | }
|
|---|
| 658 | setIdentifierSyntax();
|
|---|
| 659 | return true;
|
|---|
| 660 | },
|
|---|
| 661 |
|
|---|
| 662 | // centralized error and exceptions handling
|
|---|
| 663 | emit =
|
|---|
| 664 | function(message, proto) {
|
|---|
| 665 | var err;
|
|---|
| 666 | if (Config.VERBOSITY) {
|
|---|
| 667 | if (proto) {
|
|---|
| 668 | err = new proto(message);
|
|---|
| 669 | } else {
|
|---|
| 670 | err = new global.DOMException(message, 'SyntaxError');
|
|---|
| 671 | }
|
|---|
| 672 | throw err;
|
|---|
| 673 | }
|
|---|
| 674 | if (Config.LOGERRORS && console && console.log) {
|
|---|
| 675 | console.log(message);
|
|---|
| 676 | }
|
|---|
| 677 | },
|
|---|
| 678 |
|
|---|
| 679 | // execute the engine initialization code
|
|---|
| 680 | initialize =
|
|---|
| 681 | function(doc) {
|
|---|
| 682 | setIdentifierSyntax();
|
|---|
| 683 | lastContext = switchContext(doc, true);
|
|---|
| 684 | },
|
|---|
| 685 |
|
|---|
| 686 | // build validation regexps used by the engine
|
|---|
| 687 | setIdentifierSyntax =
|
|---|
| 688 | function() {
|
|---|
| 689 |
|
|---|
| 690 | //
|
|---|
| 691 | // NOTE: SPECIAL CASES IN CSS SYNTAX PARSING RULES
|
|---|
| 692 | //
|
|---|
| 693 | // The <EOF-token> https://drafts.csswg.org/css-syntax/#typedef-eof-token
|
|---|
| 694 | // allow mangled|unclosed selector syntax at the end of selectors strings
|
|---|
| 695 | //
|
|---|
| 696 | // Literal equivalent hex representations of the characters: " ' ` ] )
|
|---|
| 697 | //
|
|---|
| 698 | // \\x22 = " - double quotes \\x5b = [ - open square bracket
|
|---|
| 699 | // \\x27 = ' - single quote \\x5d = ] - closed square bracket
|
|---|
| 700 | // \\x60 = ` - back tick \\x28 = ( - open round parens
|
|---|
| 701 | // \\x5c = \ - back slash \\x29 = ) - closed round parens
|
|---|
| 702 | //
|
|---|
| 703 | // using hex format prevents false matches of opened/closed instances
|
|---|
| 704 | // pairs, coloring breakage and other editors highlightning problems.
|
|---|
| 705 | //
|
|---|
| 706 |
|
|---|
| 707 | var
|
|---|
| 708 |
|
|---|
| 709 | // non-ascii chars
|
|---|
| 710 | noascii = '[^\\x00-\\x9f]',
|
|---|
| 711 | // escaped chars
|
|---|
| 712 | escaped = '\\\\[^\\r\\n\\f0-9a-fA-F]',
|
|---|
| 713 | // unicode chars
|
|---|
| 714 | unicode = '\\\\[0-9a-fA-F]{1,6}(?:\\r\\n|\\s)?',
|
|---|
| 715 |
|
|---|
| 716 | // can start with single/double dash
|
|---|
| 717 | // but it can not start with a digit
|
|---|
| 718 | identifier = '-?(?:[a-zA-Z_-]|' + noascii + '|' + escaped + '|' + unicode + ')' +
|
|---|
| 719 | '(?:-{2}|[0-9]|[a-zA-Z_-]|' + noascii + '|' + escaped + '|' + unicode + ')*',
|
|---|
| 720 |
|
|---|
| 721 | pseudonames = '[-\\w]+',
|
|---|
| 722 | pseudoparms = '(?:[-+]?\\d*)(?:n\\s?[-+]?\\s?\\d*)',
|
|---|
| 723 | doublequote = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*(?:"|$)',
|
|---|
| 724 | singlequote = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*(?:'|$)",
|
|---|
| 725 |
|
|---|
| 726 | attrparser = identifier + '|' + doublequote + '|' + singlequote,
|
|---|
| 727 |
|
|---|
| 728 | attrvalues = '([\\x22\\x27]?)((?!\\3)*|(?:\\\\?.)*?)(?:\\3|$)',
|
|---|
| 729 |
|
|---|
| 730 | attributes =
|
|---|
| 731 | '\\[' +
|
|---|
| 732 | // attribute presence
|
|---|
| 733 | '(?:\\*\\|)?' +
|
|---|
| 734 | WSP + '?' +
|
|---|
| 735 | '(' + identifier + '(?::' + identifier + ')?)' +
|
|---|
| 736 | WSP + '?' +
|
|---|
| 737 | '(?:' +
|
|---|
| 738 | '(' + CFG.operators + ')' + WSP + '?' +
|
|---|
| 739 | '(?:' + attrparser + ')' +
|
|---|
| 740 | ')?' +
|
|---|
| 741 | // attribute case sensitivity
|
|---|
| 742 | '(?:' + WSP + '?\\b(i))?' + WSP + '?' +
|
|---|
| 743 | '(?:\\]|$)',
|
|---|
| 744 |
|
|---|
| 745 | attrmatcher = attributes.replace(attrparser, attrvalues),
|
|---|
| 746 |
|
|---|
| 747 | pseudoclass =
|
|---|
| 748 | '(?:\\x28' + WSP + '*' +
|
|---|
| 749 | '(?:' + pseudoparms + '?)?|' +
|
|---|
| 750 | // universal * &
|
|---|
| 751 | // namespace *|*
|
|---|
| 752 | '(?:\\*|\\*\\|)|' +
|
|---|
| 753 | '(?:' +
|
|---|
| 754 | '(?::' + pseudonames +
|
|---|
| 755 | '(?:\\x28' + pseudoparms + '?(?:\\x29|$))?|' +
|
|---|
| 756 | ')|' +
|
|---|
| 757 | '(?:[.#]?' + identifier + ')|' +
|
|---|
| 758 | '(?:' + attributes + ')' +
|
|---|
| 759 | ')+|' +
|
|---|
| 760 | '(?:' + WSP + '?[>+~][^>+~]' + WSP + '?)|' +
|
|---|
| 761 | '(?:' + WSP + '?,' + WSP + '?)|' +
|
|---|
| 762 | '(?:' + WSP + '?)|' +
|
|---|
| 763 | '(?:\\x29|$)' +
|
|---|
| 764 | ')*',
|
|---|
| 765 |
|
|---|
| 766 | standardValidator =
|
|---|
| 767 | '(?=' + WSP + '?[^>+~(){}<>])' +
|
|---|
| 768 | '(?:' +
|
|---|
| 769 | // universal * &
|
|---|
| 770 | // namespace *|*
|
|---|
| 771 | '(?:\\*|\\*\\|)|' +
|
|---|
| 772 | '(?:[.#]?' + identifier + ')+|' +
|
|---|
| 773 | '(?:' + attributes + ')+|' +
|
|---|
| 774 | '(?:::?' + pseudonames + pseudoclass + ')|' +
|
|---|
| 775 | '(?:' + WSP + '?' + CFG.combinators + WSP + '?)|' +
|
|---|
| 776 | '(?:' + WSP + '?,' + WSP + '?)|' +
|
|---|
| 777 | '(?:' + WSP + '?)' +
|
|---|
| 778 | ')+';
|
|---|
| 779 |
|
|---|
| 780 | // the following global RE is used to return the
|
|---|
| 781 | // deepest localName in selector strings and then
|
|---|
| 782 | // use it to retrieve all possible matching nodes
|
|---|
| 783 | // that will be filtered by compiled resolvers
|
|---|
| 784 | reOptimizer = RegExp(
|
|---|
| 785 | '(?:([.:#*]?)' +
|
|---|
| 786 | '(' + identifier + ')' +
|
|---|
| 787 | '(?:' +
|
|---|
| 788 | ':[-\\w]+|' +
|
|---|
| 789 | '\\[[^\\]]+(?:\\]|$)|' +
|
|---|
| 790 | '\\x28[^\\x29]+(?:\\x29|$)' +
|
|---|
| 791 | ')*)$');
|
|---|
| 792 |
|
|---|
| 793 | // global
|
|---|
| 794 | reValidator = RegExp(standardValidator, 'g');
|
|---|
| 795 |
|
|---|
| 796 | Patterns.id = RegExp('^#(' + identifier + ')(.*)');
|
|---|
| 797 | Patterns.tagName = RegExp('^(' + identifier + ')(.*)');
|
|---|
| 798 | Patterns.className = RegExp('^\\.(' + identifier + ')(.*)');
|
|---|
| 799 | Patterns.attribute = RegExp('^(?:' + attrmatcher + ')(.*)');
|
|---|
| 800 | },
|
|---|
| 801 |
|
|---|
| 802 | F_INIT = '"use strict";return function Resolver(c,f,x,r)',
|
|---|
| 803 |
|
|---|
| 804 | /*
|
|---|
| 805 | // S - M - N
|
|---|
| 806 | //
|
|---|
| 807 | // SELECT
|
|---|
| 808 | // MATCH
|
|---|
| 809 | // NONE
|
|---|
| 810 | //
|
|---|
| 811 | */
|
|---|
| 812 |
|
|---|
| 813 | S_HEAD = 'var e,n,o,j=r.length-1,k=-1',
|
|---|
| 814 | M_HEAD = 'var e,n,o',
|
|---|
| 815 | N_HEAD = 'var e,n,o',
|
|---|
| 816 |
|
|---|
| 817 | S_LOOP = 'main:while((e=c[++k]))',
|
|---|
| 818 | M_LOOP = 'e=c;',
|
|---|
| 819 | N_LOOP = 'main:while((e=c.item(++k)))',
|
|---|
| 820 |
|
|---|
| 821 | S_BODY = 'r[++j]=c[k];',
|
|---|
| 822 | M_BODY = '',
|
|---|
| 823 | N_BODY = 'r[++j]=c.item(k);',
|
|---|
| 824 |
|
|---|
| 825 | S_TAIL = 'continue main;',
|
|---|
| 826 | M_TAIL = 'r=true;',
|
|---|
| 827 | N_TAIL = 'r=true;',
|
|---|
| 828 |
|
|---|
| 829 | S_TEST = 'if(f(c[k])){break main;}',
|
|---|
| 830 | M_TEST = 'f(c);',
|
|---|
| 831 | N_TEST = 'if(f(c.item(k))){break main;}',
|
|---|
| 832 |
|
|---|
| 833 | S_VARS = [ ],
|
|---|
| 834 | M_VARS = [ ],
|
|---|
| 835 | N_VARS = [ ],
|
|---|
| 836 |
|
|---|
| 837 | // compile groups or single selector strings into
|
|---|
| 838 | // executable functions for matching or selecting
|
|---|
| 839 |
|
|---|
| 840 | S_TEST = 'if(f(c[k])){break main;}',
|
|---|
| 841 | M_TEST = 'f(c);',
|
|---|
| 842 | N_TEST = 'if(f(c.item(k))){break main;}',
|
|---|
| 843 |
|
|---|
| 844 | S_VARS = [ ],
|
|---|
| 845 | M_VARS = [ ],
|
|---|
| 846 | N_VARS = [ ],
|
|---|
| 847 |
|
|---|
| 848 | // compile groups or single selector strings into
|
|---|
| 849 | // executable functions for matching or selecting
|
|---|
| 850 | compile =
|
|---|
| 851 | function(selector, mode, callback) {
|
|---|
| 852 | var factory, token, head = '', loop = '', macro = '', source = '', vars = '';
|
|---|
| 853 |
|
|---|
| 854 | // 'mode' can be boolean or null
|
|---|
| 855 | // true = select / false = match
|
|---|
| 856 | // null to use collection.item()
|
|---|
| 857 | switch (mode) {
|
|---|
| 858 | case true:
|
|---|
| 859 | if (selectLambdas[selector]) { return selectLambdas[selector]; }
|
|---|
| 860 | macro = S_BODY + (callback ? S_TEST : '') + S_TAIL;
|
|---|
| 861 | head = S_HEAD;
|
|---|
| 862 | loop = S_LOOP;
|
|---|
| 863 | break;
|
|---|
| 864 | case false:
|
|---|
| 865 | if (matchLambdas[selector]) { return matchLambdas[selector]; }
|
|---|
| 866 | macro = M_BODY + (callback ? M_TEST : '') + M_TAIL;
|
|---|
| 867 | head = M_HEAD;
|
|---|
| 868 | loop = M_LOOP;
|
|---|
| 869 | break;
|
|---|
| 870 | case null:
|
|---|
| 871 | if (selectLambdas[selector]) { return selectLambdas[selector]; }
|
|---|
| 872 | macro = N_BODY + (callback ? N_TEST : '') + N_TAIL;
|
|---|
| 873 | head = N_HEAD;
|
|---|
| 874 | loop = N_LOOP;
|
|---|
| 875 | break;
|
|---|
| 876 | default:
|
|---|
| 877 | break;
|
|---|
| 878 | }
|
|---|
| 879 |
|
|---|
| 880 | source = compileSelector(selector, macro, mode, callback);
|
|---|
| 881 |
|
|---|
| 882 | loop += mode || mode === null ? '{' + source + '}' : source;
|
|---|
| 883 |
|
|---|
| 884 | if (mode || mode === null && selector.includes(':nth')) {
|
|---|
| 885 | loop += reNthElem.test(selector) ? 's.nthElement(null, 2);' : '';
|
|---|
| 886 | loop += reNthType.test(selector) ? 's.nthOfType(null, 2);' : '';
|
|---|
| 887 | }
|
|---|
| 888 |
|
|---|
| 889 | if (S_VARS[0] || M_VARS[0] || N_VARS[0]) {
|
|---|
| 890 | vars = ',' + (S_VARS.join(',') || M_VARS.join(',') || N_VARS[0]);
|
|---|
| 891 | S_VARS.length = 0;
|
|---|
| 892 | M_VARS.length = 0;
|
|---|
| 893 | N_VARS.length = 0;
|
|---|
| 894 | }
|
|---|
| 895 |
|
|---|
| 896 | factory = Function('s', F_INIT + '{' + head + vars + ';' + loop + 'return r;}')(Snapshot);
|
|---|
| 897 |
|
|---|
| 898 | return mode || mode === null ? (selectLambdas[selector] = factory) : (matchLambdas[selector] = factory);
|
|---|
| 899 | },
|
|---|
| 900 |
|
|---|
| 901 | // build conditional code to check components of selector strings
|
|---|
| 902 | compileSelector =
|
|---|
| 903 | function(expression, source, mode, callback) {
|
|---|
| 904 |
|
|---|
| 905 | var a, b, n, f, name, NS, referenceElement,
|
|---|
| 906 | compat, expr, match, result, status, symbol, test,
|
|---|
| 907 | type, selector = expression, vars;
|
|---|
| 908 |
|
|---|
| 909 | // isolate selector combinators
|
|---|
| 910 | selector = selector.replace(STD.combinator, '$1');
|
|---|
| 911 |
|
|---|
| 912 | // javascript needs a label to break
|
|---|
| 913 | // out of the while loops processing
|
|---|
| 914 | selector_recursion_label:
|
|---|
| 915 |
|
|---|
| 916 | while (selector) {
|
|---|
| 917 |
|
|---|
| 918 | // get namespace prefix if present or get first char of selector
|
|---|
| 919 | symbol = STD.apimethods.test(selector) ? '|' : selector[0];
|
|---|
| 920 |
|
|---|
| 921 | switch (symbol) {
|
|---|
| 922 |
|
|---|
| 923 | // universal resolver
|
|---|
| 924 | case '*':
|
|---|
| 925 | match = selector.match(Patterns.universal);
|
|---|
| 926 | break;
|
|---|
| 927 |
|
|---|
| 928 | // id resolver
|
|---|
| 929 | case '#':
|
|---|
| 930 | match = selector.match(Patterns.id);
|
|---|
| 931 | source = 'if((/^' + match[1] + '$/.test(e.getAttribute("id")))){' + source + '}';
|
|---|
| 932 | break;
|
|---|
| 933 |
|
|---|
| 934 | // class name resolver
|
|---|
| 935 | case '.':
|
|---|
| 936 | match = selector.match(Patterns.className);
|
|---|
| 937 | compat = (QUIRKS_MODE ? 'i' : '') + '.test(e.getAttribute("class"))';
|
|---|
| 938 | source = 'if((/(^|\\s)' + match[1] + '(\\s|$)/' + compat + ')){' + source + '}';
|
|---|
| 939 | break;
|
|---|
| 940 |
|
|---|
| 941 | // tag name resolver
|
|---|
| 942 | case (/[_a-z]/i.test(symbol) ? symbol : undefined):
|
|---|
| 943 | match = selector.match(Patterns.tagName);
|
|---|
| 944 | source = 'if((e.localName=="' + match[1] + '")){' + source + '}';
|
|---|
| 945 | break;
|
|---|
| 946 |
|
|---|
| 947 | // namespace resolver
|
|---|
| 948 | case '|':
|
|---|
| 949 | match = selector.match(Patterns.namespace);
|
|---|
| 950 | if (match[1] == '*') {
|
|---|
| 951 | source = 'if(true){' + source + '}';
|
|---|
| 952 | } else if (!match[1]) {
|
|---|
| 953 | source = 'if((!e.namespaceURI)){' + source + '}';
|
|---|
| 954 | } else if (typeof match[1] == 'string' && root.prefix == match[1]) {
|
|---|
| 955 | source = 'if((e.namespaceURI=="' + NAMESPACE + '")){' + source + '}';
|
|---|
| 956 | } else {
|
|---|
| 957 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 958 | }
|
|---|
| 959 | break;
|
|---|
| 960 |
|
|---|
| 961 | // attributes resolver
|
|---|
| 962 | case '[':
|
|---|
| 963 | match = selector.match(Patterns.attribute);
|
|---|
| 964 | NS = match[0].match(STD.namespaces);
|
|---|
| 965 | name = match[1];
|
|---|
| 966 | expr = name.split(':');
|
|---|
| 967 | expr = expr.length == 2 ? expr[1] : expr[0];
|
|---|
| 968 | if (match[2] && !(test = Operators[match[2]])) {
|
|---|
| 969 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 970 | return '';
|
|---|
| 971 | }
|
|---|
| 972 | if (match[4] === '') {
|
|---|
| 973 | test = match[2] == '~=' ?
|
|---|
| 974 | { p1: '^\\s', p2: '+$', p3: 'true' } :
|
|---|
| 975 | match[2] in ATTR_STD_OPS && match[2] != '~=' ?
|
|---|
| 976 | { p1: '^', p2: '$', p3: 'true' } : test;
|
|---|
| 977 | } else if (match[2] == '~=' && match[4].includes(' ')) {
|
|---|
| 978 | // whitespace separated list but value contains space
|
|---|
| 979 | break;
|
|---|
| 980 | } else if (match[4]) {
|
|---|
| 981 | match[4] = convertEscapes(match[4]).replace(REX.RegExpChar, '\\$&');
|
|---|
| 982 | }
|
|---|
| 983 | type = match[5] == 'i' || (HTML_DOCUMENT && HTML_TABLE[expr.toLowerCase()]) ? 'i' : '';
|
|---|
| 984 | source = 'if((' +
|
|---|
| 985 | (!match[2] ? (NS ? 's.hasAttributeNS(e,"' + name + '")' : 'e.hasAttribute&&e.hasAttribute("' + name + '")') :
|
|---|
| 986 | !match[4] && ATTR_STD_OPS[match[2]] && match[2] != '~=' ? 'e.getAttribute&&e.getAttribute("' + name + '")==""' :
|
|---|
| 987 | '(/' + test.p1 + match[4] + test.p2 + '/' + type + ').test(e.getAttribute&&e.getAttribute("' + name + '"))==' + test.p3) +
|
|---|
| 988 | ')){' + source + '}';
|
|---|
| 989 | break;
|
|---|
| 990 |
|
|---|
| 991 | // *** General sibling combinator
|
|---|
| 992 | // E ~ F (F relative sibling of E)
|
|---|
| 993 | case '~':
|
|---|
| 994 | match = selector.match(Patterns.relative);
|
|---|
| 995 | source = 'while(e&&(e=e.previousElementSibling)){' + source + '}';
|
|---|
| 996 | break;
|
|---|
| 997 |
|
|---|
| 998 | // *** Adjacent sibling combinator
|
|---|
| 999 | // E + F (F adiacent sibling of E)
|
|---|
| 1000 | case '+':
|
|---|
| 1001 | match = selector.match(Patterns.adjacent);
|
|---|
| 1002 | source = 'if(e&&(e=e.previousElementSibling)){' + source + '}';
|
|---|
| 1003 | break;
|
|---|
| 1004 |
|
|---|
| 1005 | // *** Descendant combinator
|
|---|
| 1006 | // E F (E ancestor of F)
|
|---|
| 1007 | case '\x09':
|
|---|
| 1008 | case '\x20':
|
|---|
| 1009 | match = selector.match(Patterns.ancestor);
|
|---|
| 1010 | source = 'while(e&&(e=e.parentElement)){' + source + '}';
|
|---|
| 1011 | break;
|
|---|
| 1012 |
|
|---|
| 1013 | // *** Child combinator
|
|---|
| 1014 | // E > F (F children of E)
|
|---|
| 1015 | case '>':
|
|---|
| 1016 | match = selector.match(Patterns.children);
|
|---|
| 1017 | source = 'if(e&&(e=e.parentElement)){' + source + '}';
|
|---|
| 1018 | break;
|
|---|
| 1019 |
|
|---|
| 1020 | // *** user supplied combinators extensions
|
|---|
| 1021 | case (symbol in Combinators ? symbol : undefined):
|
|---|
| 1022 | // for other registered combinators extensions
|
|---|
| 1023 | match[match.length - 1] = '*';
|
|---|
| 1024 | source = Combinators[symbol](match) + source;
|
|---|
| 1025 | break;
|
|---|
| 1026 |
|
|---|
| 1027 | // *** tree-structural pseudo-classes
|
|---|
| 1028 | // :root, :empty, :first-child, :last-child, :only-child, :first-of-type, :last-of-type, :only-of-type
|
|---|
| 1029 | case ':':
|
|---|
| 1030 | if ((match = selector.match(Patterns.structural))) {
|
|---|
| 1031 | match[1] = match[1].toLowerCase();
|
|---|
| 1032 | switch (match[1]) {
|
|---|
| 1033 | case 'scope':
|
|---|
| 1034 | // use the root (documentElement) when comparing against a document
|
|---|
| 1035 | source = 'if(e===(s.from.nodeType===9?s.root:s.from)){' + source + '}';
|
|---|
| 1036 | break;
|
|---|
| 1037 | case 'root':
|
|---|
| 1038 | // there can only be one :root element, so exit the loop once found
|
|---|
| 1039 | source = 'if((e===s.root)){' + source + (mode ? 'break main;' : '') + '}';
|
|---|
| 1040 | break;
|
|---|
| 1041 | case 'empty':
|
|---|
| 1042 | // matches elements that don't contain elements or text nodes
|
|---|
| 1043 | source = 'n=e.firstChild;while(n&&!(/1|3/).test(n.nodeType)){n=n.nextSibling}if(!n){' + source + '}';
|
|---|
| 1044 | break;
|
|---|
| 1045 |
|
|---|
| 1046 | // *** child-indexed pseudo-classes
|
|---|
| 1047 | // :first-child, :last-child, :only-child
|
|---|
| 1048 | case 'only-child':
|
|---|
| 1049 | source = 'if((!e.nextElementSibling&&!e.previousElementSibling)){' + source + '}';
|
|---|
| 1050 | break;
|
|---|
| 1051 | case 'last-child':
|
|---|
| 1052 | source = 'if((!e.nextElementSibling)){' + source + '}';
|
|---|
| 1053 | break;
|
|---|
| 1054 | case 'first-child':
|
|---|
| 1055 | source = 'if((!e.previousElementSibling)){' + source + '}';
|
|---|
| 1056 | break;
|
|---|
| 1057 |
|
|---|
| 1058 | // *** typed child-indexed pseudo-classes
|
|---|
| 1059 | // :only-of-type, :last-of-type, :first-of-type
|
|---|
| 1060 | case 'only-of-type':
|
|---|
| 1061 | source = 'o=e.localName;' +
|
|---|
| 1062 | 'n=e;while((n=n.nextElementSibling)&&n.localName!=o);if(!n){' +
|
|---|
| 1063 | 'n=e;while((n=n.previousElementSibling)&&n.localName!=o);}if(!n){' + source + '}';
|
|---|
| 1064 | break;
|
|---|
| 1065 | case 'last-of-type':
|
|---|
| 1066 | source = 'n=e;o=e.localName;while((n=n.nextElementSibling)&&n.localName!=o);if(!n){' + source + '}';
|
|---|
| 1067 | break;
|
|---|
| 1068 | case 'first-of-type':
|
|---|
| 1069 | source = 'n=e;o=e.localName;while((n=n.previousElementSibling)&&n.localName!=o);if(!n){' + source + '}';
|
|---|
| 1070 | break;
|
|---|
| 1071 | default:
|
|---|
| 1072 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1073 | break;
|
|---|
| 1074 | }
|
|---|
| 1075 | }
|
|---|
| 1076 |
|
|---|
| 1077 | // *** child-indexed & typed child-indexed pseudo-classes
|
|---|
| 1078 | // :nth-child, :nth-of-type, :nth-last-child, :nth-last-of-type
|
|---|
| 1079 | else if ((match = selector.match(Patterns.treestruct))) {
|
|---|
| 1080 | match[1] = match[1].toLowerCase();
|
|---|
| 1081 | switch (match[1]) {
|
|---|
| 1082 | case 'nth-child':
|
|---|
| 1083 | case 'nth-of-type':
|
|---|
| 1084 | case 'nth-last-child':
|
|---|
| 1085 | case 'nth-last-of-type':
|
|---|
| 1086 | expr = /-of-type/i.test(match[1]);
|
|---|
| 1087 | if (match[1] && match[2]) {
|
|---|
| 1088 | type = /last/i.test(match[1]);
|
|---|
| 1089 | if (match[2] == 'n') {
|
|---|
| 1090 | source = 'if(true){' + source + '}';
|
|---|
| 1091 | break;
|
|---|
| 1092 | } else if (match[2] == '1') {
|
|---|
| 1093 | test = type ? 'next' : 'previous';
|
|---|
| 1094 | source = expr ? 'n=e;o=e.localName;' +
|
|---|
| 1095 | 'while((n=n.' + test + 'ElementSibling)&&n.localName!=o);if(!n){' + source + '}' :
|
|---|
| 1096 | 'if(!e.' + test + 'ElementSibling){' + source + '}';
|
|---|
| 1097 | break;
|
|---|
| 1098 | } else if (match[2] == 'even' || match[2] == '2n0' || match[2] == '2n+0' || match[2] == '2n') {
|
|---|
| 1099 | test = 'n%2==0';
|
|---|
| 1100 | } else if (match[2] == 'odd' || match[2] == '2n1' || match[2] == '2n+1') {
|
|---|
| 1101 | test = 'n%2==1';
|
|---|
| 1102 | } else {
|
|---|
| 1103 | f = /n/i.test(match[2]);
|
|---|
| 1104 | n = match[2].split('n');
|
|---|
| 1105 | a = parseInt(n[0], 10) || 0;
|
|---|
| 1106 | b = parseInt(n[1], 10) || 0;
|
|---|
| 1107 | if (n[0] == '-') { a = -1; }
|
|---|
| 1108 | if (n[0] == '+') { a = +1; }
|
|---|
| 1109 | test = (b ? '(n' + (b > 0 ? '-' : '+') + Math.abs(b) + ')' : 'n') + '%' + a + '==0' ;
|
|---|
| 1110 | test =
|
|---|
| 1111 | a >= +1 ? (f ? 'n>' + (b - 1) + (Math.abs(a) != 1 ? '&&' + test : '') : 'n==' + a) :
|
|---|
| 1112 | a <= -1 ? (f ? 'n<' + (b + 1) + (Math.abs(a) != 1 ? '&&' + test : '') : 'n==' + a) :
|
|---|
| 1113 | a === 0 ? (n[0] ? 'n==' + b : 'n>' + (b - 1)) : 'false';
|
|---|
| 1114 | }
|
|---|
| 1115 | expr = expr ? 'OfType' : 'Element';
|
|---|
| 1116 | type = type ? 'true' : 'false';
|
|---|
| 1117 | source = 'n=s.nth' + expr + '(e,' + type + ');if((' + test + ')){' + source + '}';
|
|---|
| 1118 | } else {
|
|---|
| 1119 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1120 | }
|
|---|
| 1121 | break;
|
|---|
| 1122 | default:
|
|---|
| 1123 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1124 | break;
|
|---|
| 1125 | }
|
|---|
| 1126 | }
|
|---|
| 1127 |
|
|---|
| 1128 | // *** logical combination pseudo-classes
|
|---|
| 1129 | // :is( s1, [ s2, ... ]), :not( s1, [ s2, ... ]),
|
|---|
| 1130 | // :has( s1, [ s2, ... ]) no nesting is allowed for
|
|---|
| 1131 | // :where( s1, [ s2, ... ]), :matches( s1, [ s2, ... ]),
|
|---|
| 1132 | else if ((match = selector.match(Patterns.logicalsel))) {
|
|---|
| 1133 | match[1] = match[1].toLowerCase();
|
|---|
| 1134 | expr = match[2]
|
|---|
| 1135 | .replace(REX.CommaGroup, ',')
|
|---|
| 1136 | .replace(REX.TrimSpaces, '')
|
|---|
| 1137 | .replace(/\x22/g, '\\"');
|
|---|
| 1138 | switch (match[1]) {
|
|---|
| 1139 | case 'is':
|
|---|
| 1140 | case 'where':
|
|---|
| 1141 | if (Config.FORGIVING) {
|
|---|
| 1142 | source =
|
|---|
| 1143 | 'try{' +
|
|---|
| 1144 | 'if(s.match("' + expr + '",e)){' + source + '}' +
|
|---|
| 1145 | '}catch(E){}';
|
|---|
| 1146 | } else {
|
|---|
| 1147 | source = 'if(s.match("' + expr + '",e)){' + source + '}';
|
|---|
| 1148 | }
|
|---|
| 1149 | break;
|
|---|
| 1150 | case 'matches':
|
|---|
| 1151 | source = 'if(s.match("' + expr + '",e)){' + source + '}';
|
|---|
| 1152 | break;
|
|---|
| 1153 | case 'not':
|
|---|
| 1154 | source = 'if(!s.match("' + expr + '",e)){' + source + '}';
|
|---|
| 1155 | break;
|
|---|
| 1156 | case 'has':
|
|---|
| 1157 | if (/^\s*(\+|\~)/.test(match[2])) {
|
|---|
| 1158 | source = 'if(e.parentElement&&Array.from(e.parentElement' +
|
|---|
| 1159 | (/^\s*[+]/.test(match[2]) ?
|
|---|
| 1160 | '.querySelectorAll("*' + expr + '")' : '.children') +
|
|---|
| 1161 | ').includes(e.nextElementSibling)){' + source + '}';
|
|---|
| 1162 | } else {
|
|---|
| 1163 | source = 'if(e.querySelector(":scope ' + expr + '"))' +
|
|---|
| 1164 | '{' + source + '}';
|
|---|
| 1165 | }
|
|---|
| 1166 | break;
|
|---|
| 1167 | default:
|
|---|
| 1168 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1169 | break;
|
|---|
| 1170 | }
|
|---|
| 1171 | }
|
|---|
| 1172 |
|
|---|
| 1173 | // *** linguistic pseudo-classes
|
|---|
| 1174 | // :dir( ltr / rtl ), :lang( en )
|
|---|
| 1175 | else if ((match = selector.match(Patterns.linguistic))) {
|
|---|
| 1176 | match[1] = match[1].toLowerCase();
|
|---|
| 1177 | switch (match[1]) {
|
|---|
| 1178 | case 'dir':
|
|---|
| 1179 | source = 'var p;if((' +
|
|---|
| 1180 | '(/' + match[2] + '/i.test(e.dir))||(p=s.ancestor("[dir]", e))&&' +
|
|---|
| 1181 | '(/' + match[2] + '/i.test(p.dir))||(e.dir==""||e.dir=="auto")&&' +
|
|---|
| 1182 | '(' + (match[2] == 'ltr' ? '!':'')+ RTL +'.test(e.textContent)))' +
|
|---|
| 1183 | '){' + source + '};';
|
|---|
| 1184 | break;
|
|---|
| 1185 | case 'lang':
|
|---|
| 1186 | expr = '(?:^|-)' + match[2] + '(?:-|$)';
|
|---|
| 1187 | source = 'var p;if((' +
|
|---|
| 1188 | '(e.isConnected&&(e.lang==""&&(p=s.ancestor("[lang]",e)))&&' +
|
|---|
| 1189 | '(p.lang=="' + match[2] + '")||/'+ expr +'/i.test(e.lang)))' +
|
|---|
| 1190 | '){' + source + '};';
|
|---|
| 1191 | break;
|
|---|
| 1192 | default:
|
|---|
| 1193 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1194 | break;
|
|---|
| 1195 | }
|
|---|
| 1196 | }
|
|---|
| 1197 |
|
|---|
| 1198 | // *** location pseudo-classes
|
|---|
| 1199 | // :any-link, :link, :visited, :target, :defined
|
|---|
| 1200 | else if ((match = selector.match(Patterns.locationpc))) {
|
|---|
| 1201 | match[1] = match[1].toLowerCase();
|
|---|
| 1202 | switch (match[1]) {
|
|---|
| 1203 | case 'any-link':
|
|---|
| 1204 | source = 'if((/^a|area$/i.test(e.localName)&&e.hasAttribute("href")||e.visited)){' + source + '}';
|
|---|
| 1205 | break;
|
|---|
| 1206 | case 'link':
|
|---|
| 1207 | source = 'if((/^a|area$/i.test(e.localName)&&e.hasAttribute("href"))){' + source + '}';
|
|---|
| 1208 | break;
|
|---|
| 1209 | case 'visited':
|
|---|
| 1210 | source = 'if((/^a|area$/i.test(e.localName)&&e.hasAttribute("href")&&e.visited)){' + source + '}';
|
|---|
| 1211 | break;
|
|---|
| 1212 | case 'target':
|
|---|
| 1213 | source = 'if(((s.doc.compareDocumentPosition(e)&16)&&s.doc.location.hash&&e.id==s.doc.location.hash.slice(1))){' + source + '}';
|
|---|
| 1214 | break;
|
|---|
| 1215 | case 'defined':
|
|---|
| 1216 | source = 'n=s.doc.defaultView.customElements.get(e.localName);if(n&&e instanceof n){' + source + '}';
|
|---|
| 1217 | break;
|
|---|
| 1218 | default:
|
|---|
| 1219 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1220 | break;
|
|---|
| 1221 | }
|
|---|
| 1222 | }
|
|---|
| 1223 |
|
|---|
| 1224 | // *** user actions pseudo-classes
|
|---|
| 1225 | // :hover, :active, :focus, :focus-visible, :focus-within
|
|---|
| 1226 | else if ((match = selector.match(Patterns.useraction))) {
|
|---|
| 1227 | match[1] = match[1].toLowerCase();
|
|---|
| 1228 | switch (match[1]) {
|
|---|
| 1229 | case 'hover':
|
|---|
| 1230 | source = 'if(e===s.HOVER){' + source + '}';
|
|---|
| 1231 | break;
|
|---|
| 1232 | case 'active':
|
|---|
| 1233 | source = 'if(e===s.doc.activeElement){' + source + '}';
|
|---|
| 1234 | break;
|
|---|
| 1235 | case 'focus':
|
|---|
| 1236 | source = 'if(s.isFocusable(e)){' + source + '}';
|
|---|
| 1237 | break;
|
|---|
| 1238 | case 'focus-visible':
|
|---|
| 1239 | source = 'if(n=s.isFocusable(e)){' +
|
|---|
| 1240 | 'if(e!==n){while(e){e=e.parentElement;if(e===n)break;}}}' +
|
|---|
| 1241 | 'if((e===n||e.autofocus)){' + source + '}';
|
|---|
| 1242 | break;
|
|---|
| 1243 | case 'focus-within':
|
|---|
| 1244 | source = 'if(n=s.isFocusable(e)){' +
|
|---|
| 1245 | 'if(n!==e){while(n){n=n.parentElement;if(n===e)break;}}}' +
|
|---|
| 1246 | 'if((n===e||n.autofocus)){' + source + '}';
|
|---|
| 1247 | break;
|
|---|
| 1248 | default:
|
|---|
| 1249 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1250 | break;
|
|---|
| 1251 | }
|
|---|
| 1252 | }
|
|---|
| 1253 |
|
|---|
| 1254 | // *** user interface and form pseudo-classes
|
|---|
| 1255 | // :enabled, :disabled, :read-only, :read-write, :placeholder-shown, :default
|
|---|
| 1256 | else if ((match = selector.match(Patterns.inputstate))) {
|
|---|
| 1257 | match[1] = match[1].toLowerCase();
|
|---|
| 1258 | switch (match[1]) {
|
|---|
| 1259 | case 'enabled':
|
|---|
| 1260 | source = 'if((("form" in e||/^optgroup$/i.test(e.localName))&&"disabled" in e &&e.disabled===false' +
|
|---|
| 1261 | ')){' + source + '}';
|
|---|
| 1262 | break;
|
|---|
| 1263 | case 'disabled':
|
|---|
| 1264 | // https://html.spec.whatwg.org/#enabling-and-disabling-form-controls:-the-disabled-attribute
|
|---|
| 1265 | source = 'if((("form" in e||/^optgroup$/i.test(e.localName))&&"disabled" in e)){' +
|
|---|
| 1266 | // F is true if any of the fieldset elements in the ancestry chain has the disabled attribute specified
|
|---|
| 1267 | // L is true if the first legend element of the fieldset contains the element
|
|---|
| 1268 | 'var x=0,N=[],F=false,L=false;' +
|
|---|
| 1269 | 'if(!(/^(optgroup|option)$/i.test(e.localName))){' +
|
|---|
| 1270 | 'n=e.parentElement;' +
|
|---|
| 1271 | 'while(n){' +
|
|---|
| 1272 | 'if(n.localName=="fieldset"){' +
|
|---|
| 1273 | 'N[x++]=n;' +
|
|---|
| 1274 | 'if(n.disabled===true){' +
|
|---|
| 1275 | 'F=true;' +
|
|---|
| 1276 | 'break;' +
|
|---|
| 1277 | '}' +
|
|---|
| 1278 | '}' +
|
|---|
| 1279 | 'n=n.parentElement;' +
|
|---|
| 1280 | '}' +
|
|---|
| 1281 | 'for(var x=0;x<N.length;x++){' +
|
|---|
| 1282 | 'if((n=s.first("legend",N[x]))&&n.contains(e)){' +
|
|---|
| 1283 | 'L=true;' +
|
|---|
| 1284 | 'break;' +
|
|---|
| 1285 | '}' +
|
|---|
| 1286 | '}' +
|
|---|
| 1287 | '}' +
|
|---|
| 1288 | 'if(e.disabled===true||(F&&!L)){' + source + '}}';
|
|---|
| 1289 | break;
|
|---|
| 1290 | case 'read-only':
|
|---|
| 1291 | source =
|
|---|
| 1292 | 'if(' +
|
|---|
| 1293 | '(/^textarea$/i.test(e.localName)&&(e.readOnly||e.disabled))||' +
|
|---|
| 1294 | '(/^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))||' +
|
|---|
| 1295 | '(!/^(?:input|textarea)$/i.test(e.localName) && !s.isContentEditable(e))' +
|
|---|
| 1296 | '){' + source + '}';
|
|---|
| 1297 | break;
|
|---|
| 1298 | case 'read-write':
|
|---|
| 1299 | source =
|
|---|
| 1300 | 'if(' +
|
|---|
| 1301 | '(/^textarea$/i.test(e.localName)&&!e.readOnly&&!e.disabled)||' +
|
|---|
| 1302 | '(/^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)||' +
|
|---|
| 1303 | '(!/^(?:input|textarea)$/i.test(e.localName) && s.isContentEditable(e))' +
|
|---|
| 1304 | '){' + source + '}';
|
|---|
| 1305 | break;
|
|---|
| 1306 | case 'placeholder-shown':
|
|---|
| 1307 | source =
|
|---|
| 1308 | 'if((' +
|
|---|
| 1309 | '(/^input|textarea$/i.test(e.localName))&&e.hasAttribute("placeholder")&&' +
|
|---|
| 1310 | '("|textarea|password|number|search|email|text|tel|url|".includes("|"+e.type+"|"))&&' +
|
|---|
| 1311 | '(!s.match(":focus",e))' +
|
|---|
| 1312 | ')){' + source + '}';
|
|---|
| 1313 | break;
|
|---|
| 1314 | case 'default':
|
|---|
| 1315 | source =
|
|---|
| 1316 | 'if(("form" in e && e.form)){' +
|
|---|
| 1317 | 'var x=0;n=[];' +
|
|---|
| 1318 | 'if(e.type=="image")n=e.form.getElementsByTagName("input");' +
|
|---|
| 1319 | 'if(e.type=="submit")n=e.form.elements;' +
|
|---|
| 1320 | 'while(n[x]&&e!==n[x]){' +
|
|---|
| 1321 | 'if(n[x].type=="image")break;' +
|
|---|
| 1322 | 'if(n[x].type=="submit")break;' +
|
|---|
| 1323 | 'x++;' +
|
|---|
| 1324 | '}' +
|
|---|
| 1325 | '}' +
|
|---|
| 1326 | 'if((e.form&&(e===n[x]&&"|image|submit|".includes("|"+e.type+"|"))||' +
|
|---|
| 1327 | '((/^option$/i.test(e.localName))&&e.defaultSelected)||' +
|
|---|
| 1328 | '(("|radio|checkbox|".includes("|"+e.type+"|"))&&e.defaultChecked)' +
|
|---|
| 1329 | ')){' + source + '}';
|
|---|
| 1330 | break;
|
|---|
| 1331 | default:
|
|---|
| 1332 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1333 | break;
|
|---|
| 1334 | }
|
|---|
| 1335 | }
|
|---|
| 1336 |
|
|---|
| 1337 | // *** input pseudo-classes (for form validation)
|
|---|
| 1338 | // :checked, :indeterminate, :valid, :invalid, :in-range, :out-of-range, :required, :optional
|
|---|
| 1339 | else if ((match = selector.match(Patterns.inputvalue))) {
|
|---|
| 1340 | match[1] = match[1].toLowerCase();
|
|---|
| 1341 | switch (match[1]) {
|
|---|
| 1342 | case 'checked':
|
|---|
| 1343 | source = 'if((/^input$/i.test(e.localName)&&' +
|
|---|
| 1344 | '("|radio|checkbox|".includes("|"+e.type+"|")&&e.checked)||' +
|
|---|
| 1345 | '(/^option$/i.test(e.localName)&&(e.selected||e.checked))' +
|
|---|
| 1346 | ')){' + source + '}';
|
|---|
| 1347 | break;
|
|---|
| 1348 | case 'indeterminate':
|
|---|
| 1349 | source =
|
|---|
| 1350 | 'if((/^progress$/i.test(e.localName)&&!e.hasAttribute("value"))||' +
|
|---|
| 1351 | '(/^input$/i.test(e.localName)&&("checkbox"==e.type&&e.indeterminate)||' +
|
|---|
| 1352 | '("radio"==e.type&&e.name&&!s.first("input[name="+e.name+"]:checked",e.form))' +
|
|---|
| 1353 | ')){' + source + '}';
|
|---|
| 1354 | break;
|
|---|
| 1355 | case 'required':
|
|---|
| 1356 | source =
|
|---|
| 1357 | 'if((/^input|select|textarea$/i.test(e.localName)&&e.required)' +
|
|---|
| 1358 | '){' + source + '}';
|
|---|
| 1359 | break;
|
|---|
| 1360 | case 'optional':
|
|---|
| 1361 | source =
|
|---|
| 1362 | 'if((/^input|select|textarea$/i.test(e.localName)&&!e.required)' +
|
|---|
| 1363 | '){' + source + '}';
|
|---|
| 1364 | break;
|
|---|
| 1365 | case 'invalid':
|
|---|
| 1366 | source =
|
|---|
| 1367 | 'if(((' +
|
|---|
| 1368 | '(/^form$/i.test(e.localName)&&!e.noValidate)||' +
|
|---|
| 1369 | '(e.willValidate&&!e.formNoValidate))&&!e.checkValidity())||' +
|
|---|
| 1370 | '(/^fieldset$/i.test(e.localName)&&s.first(":invalid",e))' +
|
|---|
| 1371 | '){' + source + '}';
|
|---|
| 1372 | break;
|
|---|
| 1373 | case 'valid':
|
|---|
| 1374 | source =
|
|---|
| 1375 | 'if(((' +
|
|---|
| 1376 | '(/^form$/i.test(e.localName)&&!e.noValidate)||' +
|
|---|
| 1377 | '(e.willValidate&&!e.formNoValidate))&&e.checkValidity())||' +
|
|---|
| 1378 | '(/^fieldset$/i.test(e.localName)&&s.first(":valid",e))' +
|
|---|
| 1379 | '){' + source + '}';
|
|---|
| 1380 | break;
|
|---|
| 1381 | case 'in-range':
|
|---|
| 1382 | source =
|
|---|
| 1383 | 'if((/^input$/i.test(e.localName))&&' +
|
|---|
| 1384 | '(e.willValidate&&!e.formNoValidate)&&' +
|
|---|
| 1385 | '(!e.validity.rangeUnderflow&&!e.validity.rangeOverflow)&&' +
|
|---|
| 1386 | '("|date|datetime-local|month|number|range|time|week|".includes("|"+e.type+"|"))&&' +
|
|---|
| 1387 | '("range"==e.type||e.getAttribute("min")||e.getAttribute("max"))' +
|
|---|
| 1388 | '){' + source + '}';
|
|---|
| 1389 | break;
|
|---|
| 1390 | case 'out-of-range':
|
|---|
| 1391 | source =
|
|---|
| 1392 | 'if((/^input$/i.test(e.localName))&&' +
|
|---|
| 1393 | '(e.willValidate&&!e.formNoValidate)&&' +
|
|---|
| 1394 | '(e.validity.rangeUnderflow||e.validity.rangeOverflow)&&' +
|
|---|
| 1395 | '("|date|datetime-local|month|number|range|time|week|".includes("|"+e.type+"|"))&&' +
|
|---|
| 1396 | '("range"==e.type||e.getAttribute("min")||e.getAttribute("max"))' +
|
|---|
| 1397 | '){' + source + '}';
|
|---|
| 1398 | break;
|
|---|
| 1399 | default:
|
|---|
| 1400 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1401 | break;
|
|---|
| 1402 | }
|
|---|
| 1403 | }
|
|---|
| 1404 |
|
|---|
| 1405 | // resources state pseudo-classes (multimedia state)
|
|---|
| 1406 | // :playing, :paused, :seeking, :buffering, :stalled, :muted, :volume-locked
|
|---|
| 1407 | else if ((match = selector.match(Patterns.rsrc_state))) {
|
|---|
| 1408 | match[1] = match[1].toLowerCase();
|
|---|
| 1409 | switch (match[1]) {
|
|---|
| 1410 | case 'playing':
|
|---|
| 1411 | source = 'if(s.isPlaying(e)){' + source + '}';
|
|---|
| 1412 | break;
|
|---|
| 1413 | case 'paused':
|
|---|
| 1414 | source = 'if(!s.isPlaying(e)){' + source + '}';
|
|---|
| 1415 | break;
|
|---|
| 1416 | case 'seeking':
|
|---|
| 1417 | source = 'if(!s.isPlaying(e)){' + source + '}';
|
|---|
| 1418 | break;
|
|---|
| 1419 | case 'buffering':
|
|---|
| 1420 | break;
|
|---|
| 1421 | case 'stalled':
|
|---|
| 1422 | break;
|
|---|
| 1423 | case 'muted':
|
|---|
| 1424 | source = 'if(e.localName=="audio"&&e.getAttribute("muted")){' + source + '}';
|
|---|
| 1425 | break;
|
|---|
| 1426 | case 'volume-locked':
|
|---|
| 1427 | break;
|
|---|
| 1428 | default:
|
|---|
| 1429 | break;
|
|---|
| 1430 | }
|
|---|
| 1431 | }
|
|---|
| 1432 |
|
|---|
| 1433 | // placeholder for parse only no-op selectors
|
|---|
| 1434 | else if ((match = selector.match(Patterns.pseudo_nop))) {
|
|---|
| 1435 | break;
|
|---|
| 1436 | }
|
|---|
| 1437 |
|
|---|
| 1438 | // allow pseudo-elements starting with single colon (:)
|
|---|
| 1439 | // :after, :before, :first-letter, :first-line
|
|---|
| 1440 | // assert: e.type is in double-colon format, like ::after
|
|---|
| 1441 | else if ((match = selector.match(Patterns.pseudo_sng))) {
|
|---|
| 1442 | source = 'if(e.element&&e.type.toLowerCase()=="' +
|
|---|
| 1443 | ':' + match[0].toLowerCase() + '"){e=e.element;' + source + '}';
|
|---|
| 1444 | }
|
|---|
| 1445 |
|
|---|
| 1446 | // allow pseudo-elements starting with double colon (::)
|
|---|
| 1447 | // ::after, ::before, ::marker, ::placeholder, ::inactive-selection, ::selection, ::-webkit-<foo-bar>
|
|---|
| 1448 | // assert: e.type is in double-colon format, like ::after
|
|---|
| 1449 | else if ((match = selector.match(Patterns.pseudo_dbl))) {
|
|---|
| 1450 | source = 'if(e.element&&e.type.toLowerCase()=="' +
|
|---|
| 1451 | match[0].toLowerCase() + '"){e=e.element;' + source + '}';
|
|---|
| 1452 | }
|
|---|
| 1453 |
|
|---|
| 1454 | else {
|
|---|
| 1455 |
|
|---|
| 1456 | // reset
|
|---|
| 1457 | expr = false;
|
|---|
| 1458 | status = false;
|
|---|
| 1459 |
|
|---|
| 1460 | // process registered selector extensions
|
|---|
| 1461 | for (expr in Selectors) {
|
|---|
| 1462 | if ((match = selector.match(Selectors[expr].Expression))) {
|
|---|
| 1463 | result = Selectors[expr].Callback(match, source, mode, callback);
|
|---|
| 1464 | if ('match' in result) { match = result.match; }
|
|---|
| 1465 | vars = result.modvar;
|
|---|
| 1466 | if (mode) {
|
|---|
| 1467 | // add extra select() vars
|
|---|
| 1468 | vars && S_VARS.indexOf(vars) < 0 && (S_VARS[S_VARS.length] = vars);
|
|---|
| 1469 | } else {
|
|---|
| 1470 | // add extra match() vars
|
|---|
| 1471 | vars && M_VARS.indexOf(vars) < 0 && (M_VARS[M_VARS.length] = vars);
|
|---|
| 1472 | }
|
|---|
| 1473 | // extension source code
|
|---|
| 1474 | source = result.source;
|
|---|
| 1475 | // extension status code
|
|---|
| 1476 | status = result.status;
|
|---|
| 1477 | // break on status error
|
|---|
| 1478 | if (status) { break; }
|
|---|
| 1479 | }
|
|---|
| 1480 | }
|
|---|
| 1481 |
|
|---|
| 1482 | if (!status) {
|
|---|
| 1483 | if (Config.FORGIVING &&
|
|---|
| 1484 | selector.match(/(:(?:is|where)\x28)/)) {
|
|---|
| 1485 | return '';
|
|---|
| 1486 | }
|
|---|
| 1487 | emit('unknown pseudo-class selector \'' + selector + '\'');
|
|---|
| 1488 | return '';
|
|---|
| 1489 | }
|
|---|
| 1490 |
|
|---|
| 1491 | if (!expr) {
|
|---|
| 1492 | if (Config.FORGIVING &&
|
|---|
| 1493 | selector.match(/(:(?:is|where)\x28)/)) {
|
|---|
| 1494 | return '';
|
|---|
| 1495 | }
|
|---|
| 1496 | emit('unknown token in selector \'' + selector + '\'');
|
|---|
| 1497 | return '';
|
|---|
| 1498 | }
|
|---|
| 1499 |
|
|---|
| 1500 | }
|
|---|
| 1501 | break;
|
|---|
| 1502 |
|
|---|
| 1503 | default:
|
|---|
| 1504 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1505 | break selector_recursion_label;
|
|---|
| 1506 |
|
|---|
| 1507 | }
|
|---|
| 1508 | // end of switch symbol
|
|---|
| 1509 |
|
|---|
| 1510 | if (!match) {
|
|---|
| 1511 | if (Config.FORGIVING &&
|
|---|
| 1512 | selector.match(/(:(?:is|where)\x28)/)) {
|
|---|
| 1513 | return '';
|
|---|
| 1514 | }
|
|---|
| 1515 | emit('\'' + expression + '\'' + qsInvalid);
|
|---|
| 1516 | return '';
|
|---|
| 1517 | }
|
|---|
| 1518 |
|
|---|
| 1519 | // pop last component
|
|---|
| 1520 | selector = match.pop();
|
|---|
| 1521 | }
|
|---|
| 1522 | // end of while selector
|
|---|
| 1523 |
|
|---|
| 1524 | return source;
|
|---|
| 1525 | },
|
|---|
| 1526 |
|
|---|
| 1527 | // replace :scope context element as a
|
|---|
| 1528 | // a reference in the selector string
|
|---|
| 1529 | makeref =
|
|---|
| 1530 | function(selectors, element) {
|
|---|
| 1531 | // replace DOCUMENT with first element (root)
|
|---|
| 1532 | if (element.nodeType === 9) {
|
|---|
| 1533 | element = element.documentElement;
|
|---|
| 1534 | }
|
|---|
| 1535 | return selectors.replace(/:scope/i,
|
|---|
| 1536 | (element.localName) +
|
|---|
| 1537 | (element.id ? '#' + escape(element.id) : '') +
|
|---|
| 1538 | (element.className ? '.' + escape(element.classList[0]) : ''));
|
|---|
| 1539 | },
|
|---|
| 1540 |
|
|---|
| 1541 | // equivalent of w3c 'closest' method
|
|---|
| 1542 | ancestor =
|
|---|
| 1543 | function _closest(selectors, element, callback) {
|
|---|
| 1544 | parse(selectors, true);
|
|---|
| 1545 | selectors = makeref(selectors, element);
|
|---|
| 1546 | while (element) {
|
|---|
| 1547 | if (match(selectors, element, callback)) break;
|
|---|
| 1548 | element = element.parentElement;
|
|---|
| 1549 | }
|
|---|
| 1550 | return element;
|
|---|
| 1551 | },
|
|---|
| 1552 |
|
|---|
| 1553 | match_assert =
|
|---|
| 1554 | function(f, element, callback) {
|
|---|
| 1555 | for (var i = 0, l = f.length, r = false; l > i; ++i)
|
|---|
| 1556 | f[i](element, callback, null, false) && (r = true);
|
|---|
| 1557 | return r;
|
|---|
| 1558 | },
|
|---|
| 1559 |
|
|---|
| 1560 | match_collect =
|
|---|
| 1561 | function(selectors, callback) {
|
|---|
| 1562 | for (var i = 0, l = selectors.length, f = [ ]; l > i; ++i)
|
|---|
| 1563 | f[i] = compile(selectors[i], false, callback);
|
|---|
| 1564 | return { factory: f };
|
|---|
| 1565 | },
|
|---|
| 1566 |
|
|---|
| 1567 | // unique parser entry point for all
|
|---|
| 1568 | // methods (type matching/selecting)
|
|---|
| 1569 | parse =
|
|---|
| 1570 | function(selectors, type) {
|
|---|
| 1571 |
|
|---|
| 1572 | var parsed;
|
|---|
| 1573 |
|
|---|
| 1574 | // arguments validation
|
|---|
| 1575 | if (arguments.length === 0) {
|
|---|
| 1576 | emit(qsNotArgs, TypeError);
|
|---|
| 1577 | return Config.VERBOSITY ? undefined : (type ? none : false);
|
|---|
| 1578 | } else if (arguments[0] === '') {
|
|---|
| 1579 | emit('\'\'' + qsInvalid);
|
|---|
| 1580 | return Config.VERBOSITY ? undefined : (type ? none : false);
|
|---|
| 1581 | }
|
|---|
| 1582 |
|
|---|
| 1583 | // input NULL or UNDEFINED
|
|---|
| 1584 | if (typeof selectors != 'string') {
|
|---|
| 1585 | selectors = '' + selectors;
|
|---|
| 1586 | }
|
|---|
| 1587 |
|
|---|
| 1588 | if ((/:scope/i).test(selectors)) {
|
|---|
| 1589 | selectors = makeref(selectors, Snapshot.from);
|
|---|
| 1590 | }
|
|---|
| 1591 |
|
|---|
| 1592 | // normalize input string
|
|---|
| 1593 | parsed = unescape(selectors).
|
|---|
| 1594 | replace(/\x00|\\$/g, '\ufffd').
|
|---|
| 1595 | replace(REX.CombineWSP, '\x20').
|
|---|
| 1596 | replace(REX.PseudosWSP, '$1').
|
|---|
| 1597 | replace(REX.TabCharWSP, '\t').
|
|---|
| 1598 | replace(REX.CommaGroup, ',').
|
|---|
| 1599 | replace(REX.TrimSpaces, '');
|
|---|
| 1600 |
|
|---|
| 1601 | // parse, validate and split possible compound selectors
|
|---|
| 1602 | if ((selectors = parsed.match(reValidator)) && selectors.join('') == parsed) {
|
|---|
| 1603 | selectors = parsed.match(REX.SplitGroup);
|
|---|
| 1604 | if (parsed[parsed.length - 1] == ',') {
|
|---|
| 1605 | emit(qsInvalid);
|
|---|
| 1606 | return Config.VERBOSITY ? undefined : (type ? none : false);
|
|---|
| 1607 | }
|
|---|
| 1608 | } else {
|
|---|
| 1609 | if (Config.FORGIVING) {
|
|---|
| 1610 | // forgiving pseudos allow to continue even after parse errors
|
|---|
| 1611 | if (!(parsed.includes(':is(') || parsed.includes(':where('))) {
|
|---|
| 1612 | emit('\'' + selectors + '\'' + qsInvalid);
|
|---|
| 1613 | return Config.VERBOSITY ? undefined : (type ? none : false);
|
|---|
| 1614 | }
|
|---|
| 1615 | }
|
|---|
| 1616 | }
|
|---|
| 1617 |
|
|---|
| 1618 | return selectors;
|
|---|
| 1619 | },
|
|---|
| 1620 |
|
|---|
| 1621 | // equivalent of w3c 'matches' method
|
|---|
| 1622 | match =
|
|---|
| 1623 | function _matches(selectors, element, callback) {
|
|---|
| 1624 |
|
|---|
| 1625 | if (element && matchResolvers[selectors]) {
|
|---|
| 1626 | return match_assert(matchResolvers[selectors].factory, element, callback);
|
|---|
| 1627 | }
|
|---|
| 1628 |
|
|---|
| 1629 | matchResolvers[selectors] = match_collect(parse(selectors, false), callback);
|
|---|
| 1630 |
|
|---|
| 1631 | return match_assert(matchResolvers[selectors].factory, element, callback);
|
|---|
| 1632 | },
|
|---|
| 1633 |
|
|---|
| 1634 | // equivalent of w3c 'querySelector' method
|
|---|
| 1635 | first =
|
|---|
| 1636 | function _querySelector(selectors, context, callback) {
|
|---|
| 1637 | return select(selectors, context,
|
|---|
| 1638 | typeof callback == 'function' ?
|
|---|
| 1639 | function firstMatch(element) {
|
|---|
| 1640 | callback(element);
|
|---|
| 1641 | return false;
|
|---|
| 1642 | } :
|
|---|
| 1643 | function firstMatch() {
|
|---|
| 1644 | return false;
|
|---|
| 1645 | }
|
|---|
| 1646 | )[0] || null;
|
|---|
| 1647 | },
|
|---|
| 1648 |
|
|---|
| 1649 | // equivalent of w3c 'querySelectorAll' method
|
|---|
| 1650 | select =
|
|---|
| 1651 | function _querySelectorAll(selectors, context, callback) {
|
|---|
| 1652 |
|
|---|
| 1653 | var nodes = [ ], resolver;
|
|---|
| 1654 |
|
|---|
| 1655 | arguments.length == 0 &&
|
|---|
| 1656 | emit(qsNotArgs, TypeError);
|
|---|
| 1657 |
|
|---|
| 1658 | context || (context = doc);
|
|---|
| 1659 | lastContext !== context &&
|
|---|
| 1660 | (lastContext = switchContext(context));
|
|---|
| 1661 |
|
|---|
| 1662 | if (selectors) {
|
|---|
| 1663 | if ((resolver = selectResolvers[selectors])) {
|
|---|
| 1664 | if (resolver.context === context &&
|
|---|
| 1665 | resolver.callback === callback) {
|
|---|
| 1666 | var i, l, list,
|
|---|
| 1667 | f = resolver.factory,
|
|---|
| 1668 | h = resolver.htmlset,
|
|---|
| 1669 | n = resolver.nodeset;
|
|---|
| 1670 | if (n.length > 1) {
|
|---|
| 1671 | for (i = 0, l = n.length; l > i; ++i) {
|
|---|
| 1672 | list = compat[n[i][0]](context, n[i].slice(1))();
|
|---|
| 1673 | if (f[i] !== null) {
|
|---|
| 1674 | f[i](list, callback, context, nodes);
|
|---|
| 1675 | } else {
|
|---|
| 1676 | nodes = nodes.concat(list);
|
|---|
| 1677 | }
|
|---|
| 1678 | }
|
|---|
| 1679 | if (l > 1 && nodes.length > 1) {
|
|---|
| 1680 | nodes.sort(documentOrder);
|
|---|
| 1681 | hasDupes && (nodes = unique(nodes));
|
|---|
| 1682 | }
|
|---|
| 1683 | } else {
|
|---|
| 1684 | if (f[0]) {
|
|---|
| 1685 | nodes = f[0](h[0](), callback, context, nodes);
|
|---|
| 1686 | } else {
|
|---|
| 1687 | nodes = h[0]();
|
|---|
| 1688 | }
|
|---|
| 1689 | }
|
|---|
| 1690 | if (typeof callback == 'function') {
|
|---|
| 1691 | nodes = concatCall(nodes, callback);
|
|---|
| 1692 | }
|
|---|
| 1693 | return !Config.NODE_LIST ?
|
|---|
| 1694 | nodes : isInstanceOf(nodes) ?
|
|---|
| 1695 | nodes : toNodeList(nodes);
|
|---|
| 1696 | }
|
|---|
| 1697 | }
|
|---|
| 1698 | }
|
|---|
| 1699 |
|
|---|
| 1700 | // save/reuse factory and closure collection
|
|---|
| 1701 | selectResolvers[selectors] = collect(parse(selectors, true), context, callback);
|
|---|
| 1702 |
|
|---|
| 1703 | nodes = selectResolvers[selectors].results;
|
|---|
| 1704 |
|
|---|
| 1705 | if (typeof callback == 'function') {
|
|---|
| 1706 | nodes = concatCall(nodes, callback);
|
|---|
| 1707 | }
|
|---|
| 1708 | return !Config.NODE_LIST ?
|
|---|
| 1709 | nodes : isInstanceOf(nodes) ?
|
|---|
| 1710 | nodes : toNodeList(nodes);
|
|---|
| 1711 | },
|
|---|
| 1712 |
|
|---|
| 1713 | // optimize selectors avoiding duplicated checks
|
|---|
| 1714 | optimize =
|
|---|
| 1715 | function(selector, token) {
|
|---|
| 1716 | var index = token.index,
|
|---|
| 1717 | length = token[1].length + token[2].length;
|
|---|
| 1718 | return selector.slice(0, index) +
|
|---|
| 1719 | (' >+~'.indexOf(selector.charAt(index - 1)) > -1 ?
|
|---|
| 1720 | (':['.indexOf(selector.charAt(index + length + 1)) > -1 ?
|
|---|
| 1721 | '*' : '') : '') + selector.slice(index + length - (token[1] == '*' ? 1 : 0));
|
|---|
| 1722 | },
|
|---|
| 1723 |
|
|---|
| 1724 | // prepare factory resolvers and closure collections
|
|---|
| 1725 | collect =
|
|---|
| 1726 | function(selectors, context, callback) {
|
|---|
| 1727 |
|
|---|
| 1728 | var i, l, seen = { }, token = ['', '*', '*'], optimized = selectors,
|
|---|
| 1729 | factory = [ ], htmlset = [ ], nodeset = [ ], results = [ ], type;
|
|---|
| 1730 |
|
|---|
| 1731 | for (i = 0, l = selectors.length; l > i; ++i) {
|
|---|
| 1732 |
|
|---|
| 1733 | if (!seen[selectors[i]] && (seen[selectors[i]] = true)) {
|
|---|
| 1734 | type = selectors[i].match(reOptimizer);
|
|---|
| 1735 | if (type && type[1] != ':' && (token = type)) {
|
|---|
| 1736 | token[1] || (token[1] = '*');
|
|---|
| 1737 | optimized[i] = optimize(optimized[i], token);
|
|---|
| 1738 | } else {
|
|---|
| 1739 | token = ['', '*', '*'];
|
|---|
| 1740 | }
|
|---|
| 1741 | }
|
|---|
| 1742 |
|
|---|
| 1743 | nodeset[i] = token[1] + token[2];
|
|---|
| 1744 | token[2] = unescapeIdentifier(token[2]);
|
|---|
| 1745 | htmlset[i] = compat[token[1]](context, token[2]);
|
|---|
| 1746 | factory[i] = compile(optimized[i], true, null);
|
|---|
| 1747 |
|
|---|
| 1748 | factory[i] ?
|
|---|
| 1749 | factory[i](htmlset[i](), callback, context, results) :
|
|---|
| 1750 | results.concat(htmlset[i]());
|
|---|
| 1751 | }
|
|---|
| 1752 |
|
|---|
| 1753 | if (l > 1) {
|
|---|
| 1754 | results.sort(documentOrder);
|
|---|
| 1755 | hasDupes && (results = unique(results));
|
|---|
| 1756 | }
|
|---|
| 1757 |
|
|---|
| 1758 | return {
|
|---|
| 1759 | callback: callback,
|
|---|
| 1760 | context: context,
|
|---|
| 1761 | factory: factory,
|
|---|
| 1762 | htmlset: htmlset,
|
|---|
| 1763 | nodeset: nodeset,
|
|---|
| 1764 | results: results
|
|---|
| 1765 | };
|
|---|
| 1766 |
|
|---|
| 1767 | },
|
|---|
| 1768 |
|
|---|
| 1769 | // handlers needed for the :hover pseudo-class
|
|---|
| 1770 | // track state change in browsers and headless
|
|---|
| 1771 | initEnv =
|
|---|
| 1772 | (function() {
|
|---|
| 1773 | doc.addEventListener('mouseover', function(e) { Snapshot.HOVER = e.target; }, true);
|
|---|
| 1774 | doc.addEventListener('mouseout', function(e) { Snapshot.HOVER = null; }, true);
|
|---|
| 1775 | })(),
|
|---|
| 1776 |
|
|---|
| 1777 | // QSA placeholders to native references
|
|---|
| 1778 | _closest, _matches,
|
|---|
| 1779 | _querySelector, _querySelectorAll,
|
|---|
| 1780 | _querySelectorDoc, _querySelectorAllDoc,
|
|---|
| 1781 |
|
|---|
| 1782 | // overrides QSA methods (only for browsers)
|
|---|
| 1783 | install =
|
|---|
| 1784 | function(all) {
|
|---|
| 1785 | // save references
|
|---|
| 1786 | _closest = Element.prototype.closest;
|
|---|
| 1787 | _matches = Element.prototype.matches;
|
|---|
| 1788 |
|
|---|
| 1789 | _querySelector = Element.prototype.querySelector;
|
|---|
| 1790 | _querySelectorAll = Element.prototype.querySelectorAll;
|
|---|
| 1791 |
|
|---|
| 1792 | _querySelectorDoc = Document.prototype.querySelector;
|
|---|
| 1793 | _querySelectorAllDoc = Document.prototype.querySelectorAll;
|
|---|
| 1794 |
|
|---|
| 1795 | function parseQSArgs() {
|
|---|
| 1796 | var method = arguments[arguments.length - 1];
|
|---|
| 1797 | return (
|
|---|
| 1798 | arguments.length < 2 ?
|
|---|
| 1799 | method.apply(this, [ ]) :
|
|---|
| 1800 | arguments.length < 3 ?
|
|---|
| 1801 | method.apply(this, [ arguments[0], this ]) :
|
|---|
| 1802 | method.apply(this, [ arguments[0], this,
|
|---|
| 1803 | typeof arguments[1] == 'function' ? arguments[1] : undefined ]));
|
|---|
| 1804 | }
|
|---|
| 1805 |
|
|---|
| 1806 | Element.prototype.closest =
|
|---|
| 1807 | HTMLElement.prototype.closest =
|
|---|
| 1808 | function closest() {
|
|---|
| 1809 | return parseQSArgs.apply(this, [].slice.call(arguments).concat(ancestor));
|
|---|
| 1810 | };
|
|---|
| 1811 |
|
|---|
| 1812 | Element.prototype.matches =
|
|---|
| 1813 | HTMLElement.prototype.matches =
|
|---|
| 1814 | function matches() {
|
|---|
| 1815 | return parseQSArgs.apply(this, [].slice.call(arguments).concat(match));
|
|---|
| 1816 | };
|
|---|
| 1817 |
|
|---|
| 1818 | Element.prototype.querySelector =
|
|---|
| 1819 | HTMLElement.prototype.querySelector =
|
|---|
| 1820 | function querySelector() {
|
|---|
| 1821 | return parseQSArgs.apply(this, [].slice.call(arguments).concat(first));
|
|---|
| 1822 | };
|
|---|
| 1823 |
|
|---|
| 1824 | Element.prototype.querySelectorAll =
|
|---|
| 1825 | HTMLElement.prototype.querySelectorAll =
|
|---|
| 1826 | function querySelectorAll() {
|
|---|
| 1827 | return parseQSArgs.apply(this, [].slice.call(arguments).concat(select));
|
|---|
| 1828 | };
|
|---|
| 1829 |
|
|---|
| 1830 | Document.prototype.querySelector =
|
|---|
| 1831 | DocumentFragment.prototype.querySelector =
|
|---|
| 1832 | function querySelector() {
|
|---|
| 1833 | return parseQSArgs.apply(this, [].slice.call(arguments).concat(first));
|
|---|
| 1834 | };
|
|---|
| 1835 |
|
|---|
| 1836 | Document.prototype.querySelectorAll =
|
|---|
| 1837 | DocumentFragment.prototype.querySelectorAll =
|
|---|
| 1838 | function querySelectorAll() {
|
|---|
| 1839 | return parseQSArgs.apply(this, [].slice.call(arguments).concat(select));
|
|---|
| 1840 | };
|
|---|
| 1841 |
|
|---|
| 1842 | if (all) {
|
|---|
| 1843 | doc.addEventListener('load', function(e) {
|
|---|
| 1844 | var c, d, r, s, t = e.target;
|
|---|
| 1845 | if (/iframe/i.test(t.localName)) {
|
|---|
| 1846 | c = '(' + Export + ')(this, ' + Factory + ');'; d = t.ownerDocument;
|
|---|
| 1847 | s = d.createElement('script'); s.textContent = c + 'NW.Dom.install(true)';
|
|---|
| 1848 | r = d.documentElement; r.removeChild(r.insertBefore(s, r.firstChild));
|
|---|
| 1849 | }
|
|---|
| 1850 | }, true);
|
|---|
| 1851 | }
|
|---|
| 1852 |
|
|---|
| 1853 | },
|
|---|
| 1854 |
|
|---|
| 1855 | // restore QSA methods (only for browsers)
|
|---|
| 1856 | uninstall =
|
|---|
| 1857 | function() {
|
|---|
| 1858 | // restore references
|
|---|
| 1859 | if (_closest) {
|
|---|
| 1860 | Element.prototype.closest = _closest;
|
|---|
| 1861 | HTMLElement.prototype.closest = _closest;
|
|---|
| 1862 | }
|
|---|
| 1863 | if (_matches) {
|
|---|
| 1864 | Element.prototype.matches = _matches;
|
|---|
| 1865 | HTMLElement.prototype.matches = _matches;
|
|---|
| 1866 | }
|
|---|
| 1867 | if (_querySelector) {
|
|---|
| 1868 | Element.prototype.querySelector =
|
|---|
| 1869 | HTMLElement.prototype.querySelector = _querySelector;
|
|---|
| 1870 | Element.prototype.querySelectorAll =
|
|---|
| 1871 | HTMLElement.prototype.querySelectorAll = _querySelector;
|
|---|
| 1872 | }
|
|---|
| 1873 | if (_querySelectorAllDoc) {
|
|---|
| 1874 | Document.prototype.querySelector =
|
|---|
| 1875 | DocumentFragment.prototype.querySelector = _querySelectorDoc;
|
|---|
| 1876 | Document.prototype.querySelectorAll =
|
|---|
| 1877 | DocumentFragment.prototype.querySelectorAll = _querySelectorAllDoc;
|
|---|
| 1878 | }
|
|---|
| 1879 | },
|
|---|
| 1880 |
|
|---|
| 1881 | // empty set
|
|---|
| 1882 | none = Array(),
|
|---|
| 1883 |
|
|---|
| 1884 | // context
|
|---|
| 1885 | lastContext,
|
|---|
| 1886 |
|
|---|
| 1887 | // cached lambdas
|
|---|
| 1888 | matchLambdas = { },
|
|---|
| 1889 | selectLambdas = { },
|
|---|
| 1890 |
|
|---|
| 1891 | // cached resolvers
|
|---|
| 1892 | matchResolvers = { },
|
|---|
| 1893 | selectResolvers = { },
|
|---|
| 1894 |
|
|---|
| 1895 | // passed to resolvers
|
|---|
| 1896 | Snapshot = {
|
|---|
| 1897 |
|
|---|
| 1898 | doc: doc,
|
|---|
| 1899 | from: doc,
|
|---|
| 1900 | root: root,
|
|---|
| 1901 |
|
|---|
| 1902 | byTag: byTag,
|
|---|
| 1903 |
|
|---|
| 1904 | first: first,
|
|---|
| 1905 | match: match,
|
|---|
| 1906 |
|
|---|
| 1907 | ancestor: ancestor,
|
|---|
| 1908 |
|
|---|
| 1909 | nthOfType: nthOfType,
|
|---|
| 1910 | nthElement: nthElement,
|
|---|
| 1911 |
|
|---|
| 1912 | isFocusable: isFocusable,
|
|---|
| 1913 | isContentEditable: isContentEditable,
|
|---|
| 1914 | hasAttributeNS: hasAttributeNS
|
|---|
| 1915 | },
|
|---|
| 1916 |
|
|---|
| 1917 | // public exported methods/objects
|
|---|
| 1918 | Dom = {
|
|---|
| 1919 |
|
|---|
| 1920 | // exported cache objects
|
|---|
| 1921 |
|
|---|
| 1922 | matchLambdas: matchLambdas,
|
|---|
| 1923 | selectLambdas: selectLambdas,
|
|---|
| 1924 |
|
|---|
| 1925 | matchResolvers: matchResolvers,
|
|---|
| 1926 | selectResolvers: selectResolvers,
|
|---|
| 1927 |
|
|---|
| 1928 | // exported compiler macros
|
|---|
| 1929 |
|
|---|
| 1930 | CFG: CFG,
|
|---|
| 1931 |
|
|---|
| 1932 | S_BODY: S_BODY,
|
|---|
| 1933 | M_BODY: M_BODY,
|
|---|
| 1934 | N_BODY: M_BODY,
|
|---|
| 1935 |
|
|---|
| 1936 | S_TEST: S_TEST,
|
|---|
| 1937 | M_TEST: M_TEST,
|
|---|
| 1938 | N_TEST: N_TEST,
|
|---|
| 1939 |
|
|---|
| 1940 | // exported engine methods
|
|---|
| 1941 |
|
|---|
| 1942 | byId: byId,
|
|---|
| 1943 | byTag: byTag,
|
|---|
| 1944 | byClass: byClass,
|
|---|
| 1945 |
|
|---|
| 1946 | match: match,
|
|---|
| 1947 | first: first,
|
|---|
| 1948 | select: select,
|
|---|
| 1949 | closest: ancestor,
|
|---|
| 1950 |
|
|---|
| 1951 | compile: compile,
|
|---|
| 1952 | configure: configure,
|
|---|
| 1953 |
|
|---|
| 1954 | emit: emit,
|
|---|
| 1955 | Config: Config,
|
|---|
| 1956 | Snapshot: Snapshot,
|
|---|
| 1957 |
|
|---|
| 1958 | Version: version,
|
|---|
| 1959 |
|
|---|
| 1960 | install: install,
|
|---|
| 1961 | uninstall: uninstall,
|
|---|
| 1962 |
|
|---|
| 1963 | Operators: Operators,
|
|---|
| 1964 | Selectors: Selectors,
|
|---|
| 1965 |
|
|---|
| 1966 | // register a new selector combinator symbol and its related function resolver
|
|---|
| 1967 | registerCombinator:
|
|---|
| 1968 | function(combinator, resolver) {
|
|---|
| 1969 | var i = 0, l = combinator.length, symbol;
|
|---|
| 1970 | for (; l > i; ++i) {
|
|---|
| 1971 | if (combinator[i] != '=') {
|
|---|
| 1972 | symbol = combinator[i];
|
|---|
| 1973 | break;
|
|---|
| 1974 | }
|
|---|
| 1975 | }
|
|---|
| 1976 | if (CFG.combinators.indexOf(symbol) < 0) {
|
|---|
| 1977 | CFG.combinators = CFG.combinators.replace('](', symbol + '](');
|
|---|
| 1978 | CFG.combinators = CFG.combinators.replace('])', symbol + '])');
|
|---|
| 1979 | Combinators[combinator] = resolver;
|
|---|
| 1980 | setIdentifierSyntax();
|
|---|
| 1981 | } else {
|
|---|
| 1982 | console.warn('Warning: the \'' + combinator + '\' combinator is already registered.');
|
|---|
| 1983 | }
|
|---|
| 1984 | },
|
|---|
| 1985 |
|
|---|
| 1986 | // register a new attribute operator symbol and its related function resolver
|
|---|
| 1987 | registerOperator:
|
|---|
| 1988 | function(operator, resolver) {
|
|---|
| 1989 | var i = 0, l = operator.length, symbol;
|
|---|
| 1990 | for (; l > i; ++i) {
|
|---|
| 1991 | if (operator[i] != '=') {
|
|---|
| 1992 | symbol = operator[i];
|
|---|
| 1993 | break;
|
|---|
| 1994 | }
|
|---|
| 1995 | }
|
|---|
| 1996 | if (CFG.operators.indexOf(symbol) < 0 && !Operators[operator]) {
|
|---|
| 1997 | CFG.operators = CFG.operators.replace(']=', symbol + ']=');
|
|---|
| 1998 | Operators[operator] = resolver;
|
|---|
| 1999 | setIdentifierSyntax();
|
|---|
| 2000 | } else {
|
|---|
| 2001 | console.warn('Warning: the \'' + operator + '\' operator is already registered.');
|
|---|
| 2002 | }
|
|---|
| 2003 | },
|
|---|
| 2004 |
|
|---|
| 2005 | // register a new selector symbol and its related function resolver
|
|---|
| 2006 | registerSelector:
|
|---|
| 2007 | function(name, rexp, func) {
|
|---|
| 2008 | Selectors[name] || (Selectors[name] = {
|
|---|
| 2009 | Expression: rexp,
|
|---|
| 2010 | Callback: func
|
|---|
| 2011 | });
|
|---|
| 2012 | }
|
|---|
| 2013 | };
|
|---|
| 2014 |
|
|---|
| 2015 | initialize(doc);
|
|---|
| 2016 |
|
|---|
| 2017 | return Dom;
|
|---|
| 2018 | });
|
|---|