source: frontend/node_modules/nwsapi/src/nwsapi.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 70.4 KB
Line 
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});
Note: See TracBrowser for help on using the repository browser.