aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/@asamuzakjp/dom-selector/src/index.js
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-13 21:34:48 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-13 21:34:48 -0800
commit76cb9c2a39d477a64824a985ade40507e3bbade1 (patch)
tree41e997aa9c6f538d3a136af61dae9424db2005a9 /vanilla/node_modules/@asamuzakjp/dom-selector/src/index.js
parent819a39a21ac992b1393244a4c283bbb125208c69 (diff)
downloadneko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.gz
neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.bz2
neko-76cb9c2a39d477a64824a985ade40507e3bbade1.zip
feat(vanilla): add testing infrastructure and tests (NK-wjnczv)
Diffstat (limited to 'vanilla/node_modules/@asamuzakjp/dom-selector/src/index.js')
-rw-r--r--vanilla/node_modules/@asamuzakjp/dom-selector/src/index.js353
1 files changed, 353 insertions, 0 deletions
diff --git a/vanilla/node_modules/@asamuzakjp/dom-selector/src/index.js b/vanilla/node_modules/@asamuzakjp/dom-selector/src/index.js
new file mode 100644
index 0000000..8ec7b67
--- /dev/null
+++ b/vanilla/node_modules/@asamuzakjp/dom-selector/src/index.js
@@ -0,0 +1,353 @@
+/*!
+ * DOM Selector - A CSS selector engine.
+ * @license MIT
+ * @copyright asamuzaK (Kazz)
+ * @see {@link https://github.com/asamuzaK/domSelector/blob/main/LICENSE}
+ */
+
+/* import */
+import { LRUCache } from 'lru-cache';
+import { Finder } from './js/finder.js';
+import { filterSelector, getType, initNwsapi } from './js/utility.js';
+
+/* constants */
+import {
+ DOCUMENT_NODE,
+ DOCUMENT_FRAGMENT_NODE,
+ ELEMENT_NODE,
+ TARGET_ALL,
+ TARGET_FIRST,
+ TARGET_LINEAL,
+ TARGET_SELF
+} from './js/constant.js';
+const MAX_CACHE = 1024;
+
+/**
+ * @typedef {object} CheckResult
+ * @property {boolean} match - The match result.
+ * @property {string?} pseudoElement - The pseudo-element, if any.
+ */
+
+/* DOMSelector */
+export class DOMSelector {
+ /* private fields */
+ #window;
+ #document;
+ #finder;
+ #idlUtils;
+ #nwsapi;
+ #cache;
+
+ /**
+ * Creates an instance of DOMSelector.
+ * @param {Window} window - The window object.
+ * @param {Document} document - The document object.
+ * @param {object} [opt] - Options.
+ */
+ constructor(window, document, opt = {}) {
+ const { idlUtils } = opt;
+ this.#window = window;
+ this.#document = document ?? window.document;
+ this.#finder = new Finder(window);
+ this.#idlUtils = idlUtils;
+ this.#nwsapi = initNwsapi(window, document);
+ this.#cache = new LRUCache({
+ max: MAX_CACHE
+ });
+ }
+
+ /**
+ * Clears the internal cache of finder results.
+ * @returns {void}
+ */
+ clear = () => {
+ this.#finder.clearResults(true);
+ };
+
+ /**
+ * Checks if an element matches a CSS selector.
+ * @param {string} selector - The CSS selector to check against.
+ * @param {Element} node - The element node to check.
+ * @param {object} [opt] - Optional parameters.
+ * @returns {CheckResult} An object containing the check result.
+ */
+ check = (selector, node, opt = {}) => {
+ if (!node?.nodeType) {
+ const e = new this.#window.TypeError(`Unexpected type ${getType(node)}`);
+ return this.#finder.onError(e, opt);
+ } else if (node.nodeType !== ELEMENT_NODE) {
+ const e = new this.#window.TypeError(`Unexpected node ${node.nodeName}`);
+ return this.#finder.onError(e, opt);
+ }
+ const document = node.ownerDocument;
+ if (
+ document === this.#document &&
+ document.contentType === 'text/html' &&
+ document.documentElement &&
+ node.parentNode
+ ) {
+ const cacheKey = `check_${selector}`;
+ let filterMatches = false;
+ if (this.#cache.has(cacheKey)) {
+ filterMatches = this.#cache.get(cacheKey);
+ } else {
+ filterMatches = filterSelector(selector, TARGET_SELF);
+ this.#cache.set(cacheKey, filterMatches);
+ }
+ if (filterMatches) {
+ try {
+ const n = this.#idlUtils ? this.#idlUtils.wrapperForImpl(node) : node;
+ const match = this.#nwsapi.match(selector, n);
+ return {
+ match,
+ pseudoElement: null
+ };
+ } catch (e) {
+ // fall through
+ }
+ }
+ }
+ let res;
+ try {
+ if (this.#idlUtils) {
+ node = this.#idlUtils.wrapperForImpl(node);
+ }
+ opt.check = true;
+ opt.noexept = true;
+ opt.warn = false;
+ this.#finder.setup(selector, node, opt);
+ res = this.#finder.find(TARGET_SELF);
+ } catch (e) {
+ this.#finder.onError(e, opt);
+ }
+ return res;
+ };
+
+ /**
+ * Returns true if the element matches the selector.
+ * @param {string} selector - The CSS selector to match against.
+ * @param {Element} node - The element node to test.
+ * @param {object} [opt] - Optional parameters.
+ * @returns {boolean} `true` if the element matches, or `false` otherwise.
+ */
+ matches = (selector, node, opt = {}) => {
+ if (!node?.nodeType) {
+ const e = new this.#window.TypeError(`Unexpected type ${getType(node)}`);
+ return this.#finder.onError(e, opt);
+ } else if (node.nodeType !== ELEMENT_NODE) {
+ const e = new this.#window.TypeError(`Unexpected node ${node.nodeName}`);
+ return this.#finder.onError(e, opt);
+ }
+ const document = node.ownerDocument;
+ if (
+ document === this.#document &&
+ document.contentType === 'text/html' &&
+ document.documentElement &&
+ node.parentNode
+ ) {
+ const cacheKey = `matches_${selector}`;
+ let filterMatches = false;
+ if (this.#cache.has(cacheKey)) {
+ filterMatches = this.#cache.get(cacheKey);
+ } else {
+ filterMatches = filterSelector(selector, TARGET_SELF);
+ this.#cache.set(cacheKey, filterMatches);
+ }
+ if (filterMatches) {
+ try {
+ const n = this.#idlUtils ? this.#idlUtils.wrapperForImpl(node) : node;
+ const res = this.#nwsapi.match(selector, n);
+ return res;
+ } catch (e) {
+ // fall through
+ }
+ }
+ }
+ let res;
+ try {
+ if (this.#idlUtils) {
+ node = this.#idlUtils.wrapperForImpl(node);
+ }
+ this.#finder.setup(selector, node, opt);
+ const nodes = this.#finder.find(TARGET_SELF);
+ res = nodes.size;
+ } catch (e) {
+ this.#finder.onError(e, opt);
+ }
+ return !!res;
+ };
+
+ /**
+ * Traverses up the DOM tree to find the first node that matches the selector.
+ * @param {string} selector - The CSS selector to match against.
+ * @param {Element} node - The element from which to start traversing.
+ * @param {object} [opt] - Optional parameters.
+ * @returns {?Element} The first matching ancestor element, or `null`.
+ */
+ closest = (selector, node, opt = {}) => {
+ if (!node?.nodeType) {
+ const e = new this.#window.TypeError(`Unexpected type ${getType(node)}`);
+ return this.#finder.onError(e, opt);
+ } else if (node.nodeType !== ELEMENT_NODE) {
+ const e = new this.#window.TypeError(`Unexpected node ${node.nodeName}`);
+ return this.#finder.onError(e, opt);
+ }
+ const document = node.ownerDocument;
+ if (
+ document === this.#document &&
+ document.contentType === 'text/html' &&
+ document.documentElement &&
+ node.parentNode
+ ) {
+ const cacheKey = `closest_${selector}`;
+ let filterMatches = false;
+ if (this.#cache.has(cacheKey)) {
+ filterMatches = this.#cache.get(cacheKey);
+ } else {
+ filterMatches = filterSelector(selector, TARGET_LINEAL);
+ this.#cache.set(cacheKey, filterMatches);
+ }
+ if (filterMatches) {
+ try {
+ const n = this.#idlUtils ? this.#idlUtils.wrapperForImpl(node) : node;
+ const res = this.#nwsapi.closest(selector, n);
+ return res;
+ } catch (e) {
+ // fall through
+ }
+ }
+ }
+ let res;
+ try {
+ if (this.#idlUtils) {
+ node = this.#idlUtils.wrapperForImpl(node);
+ }
+ this.#finder.setup(selector, node, opt);
+ const nodes = this.#finder.find(TARGET_LINEAL);
+ if (nodes.size) {
+ let refNode = node;
+ while (refNode) {
+ if (nodes.has(refNode)) {
+ res = refNode;
+ break;
+ }
+ refNode = refNode.parentNode;
+ }
+ }
+ } catch (e) {
+ this.#finder.onError(e, opt);
+ }
+ return res ?? null;
+ };
+
+ /**
+ * Returns the first element within the subtree that matches the selector.
+ * @param {string} selector - The CSS selector to match.
+ * @param {Document|DocumentFragment|Element} node - The node to find within.
+ * @param {object} [opt] - Optional parameters.
+ * @returns {?Element} The first matching element, or `null`.
+ */
+ querySelector = (selector, node, opt = {}) => {
+ if (!node?.nodeType) {
+ const e = new this.#window.TypeError(`Unexpected type ${getType(node)}`);
+ return this.#finder.onError(e, opt);
+ }
+ /*
+ const document =
+ node.nodeType === DOCUMENT_NODE ? node : node.ownerDocument;
+ if (
+ document === this.#document &&
+ document.contentType === 'text/html' &&
+ document.documentElement &&
+ (node.nodeType !== DOCUMENT_FRAGMENT_NODE || !node.host)
+ ) {
+ const cacheKey = `querySelector_${selector}`;
+ let filterMatches = false;
+ if (this.#cache.has(cacheKey)) {
+ filterMatches = this.#cache.get(cacheKey);
+ } else {
+ filterMatches = filterSelector(selector, TARGET_FIRST);
+ this.#cache.set(cacheKey, filterMatches);
+ }
+ if (filterMatches) {
+ try {
+ const n = this.#idlUtils ? this.#idlUtils.wrapperForImpl(node) : node;
+ const res = this.#nwsapi.first(selector, n);
+ return res;
+ } catch (e) {
+ // fall through
+ }
+ }
+ }
+ */
+ let res;
+ try {
+ if (this.#idlUtils) {
+ node = this.#idlUtils.wrapperForImpl(node);
+ }
+ this.#finder.setup(selector, node, opt);
+ const nodes = this.#finder.find(TARGET_FIRST);
+ if (nodes.size) {
+ [res] = [...nodes];
+ }
+ } catch (e) {
+ this.#finder.onError(e, opt);
+ }
+ return res ?? null;
+ };
+
+ /**
+ * Returns an array of elements within the subtree that match the selector.
+ * Note: This method returns an Array, not a NodeList.
+ * @param {string} selector - The CSS selector to match.
+ * @param {Document|DocumentFragment|Element} node - The node to find within.
+ * @param {object} [opt] - Optional parameters.
+ * @returns {Array<Element>} An array of elements, or an empty array.
+ */
+ querySelectorAll = (selector, node, opt = {}) => {
+ if (!node?.nodeType) {
+ const e = new this.#window.TypeError(`Unexpected type ${getType(node)}`);
+ return this.#finder.onError(e, opt);
+ }
+ const document =
+ node.nodeType === DOCUMENT_NODE ? node : node.ownerDocument;
+ if (
+ document === this.#document &&
+ document.contentType === 'text/html' &&
+ document.documentElement &&
+ (node.nodeType !== DOCUMENT_FRAGMENT_NODE || !node.host)
+ ) {
+ const cacheKey = `querySelectorAll_${selector}`;
+ let filterMatches = false;
+ if (this.#cache.has(cacheKey)) {
+ filterMatches = this.#cache.get(cacheKey);
+ } else {
+ filterMatches = filterSelector(selector, TARGET_ALL);
+ this.#cache.set(cacheKey, filterMatches);
+ }
+ if (filterMatches) {
+ try {
+ const n = this.#idlUtils ? this.#idlUtils.wrapperForImpl(node) : node;
+ const res = this.#nwsapi.select(selector, n);
+ return res;
+ } catch (e) {
+ // fall through
+ }
+ }
+ }
+ let res;
+ try {
+ if (this.#idlUtils) {
+ node = this.#idlUtils.wrapperForImpl(node);
+ }
+ this.#finder.setup(selector, node, opt);
+ const nodes = this.#finder.find(TARGET_ALL);
+ if (nodes.size) {
+ res = [...nodes];
+ }
+ } catch (e) {
+ this.#finder.onError(e, opt);
+ }
+ return res ?? [];
+ };
+}