@asamuzakjp/dom-selector 2.0.3-a.2 → 2.0.3-a.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/js/finder.js CHANGED
@@ -64,11 +64,11 @@ export class Finder {
64
64
  /* private fields */
65
65
  #ast;
66
66
  #cache;
67
- #clones;
68
67
  #document;
69
68
  #finder;
70
69
  #node;
71
70
  #nodes;
71
+ #results;
72
72
  #root;
73
73
  #selector;
74
74
  #shadow;
@@ -114,7 +114,7 @@ export class Finder {
114
114
  * @param {object} node - Document, DocumentFragment, Element node
115
115
  * @param {string} selector - CSS selector
116
116
  * @param {object} opt - options
117
- * @returns {void}
117
+ * @returns {object} - node
118
118
  */
119
119
  _setup(node, selector, opt = {}) {
120
120
  const { warn } = opt;
@@ -124,7 +124,8 @@ export class Finder {
124
124
  this.#shadow = isInShadowTree(node);
125
125
  this.#selector = selector;
126
126
  [this.#ast, this.#nodes] = this._correspond(selector);
127
- this.#clones = new WeakMap();
127
+ this.#results = new WeakMap();
128
+ return this.#node;
128
129
  }
129
130
 
130
131
  /**
@@ -1686,15 +1687,61 @@ export class Finder {
1686
1687
  */
1687
1688
  _matchLeaves(leaves, node, opt) {
1688
1689
  let bool;
1689
- for (const leaf of leaves) {
1690
- bool = this._matchSelector(leaf, node, opt).has(node);
1691
- if (!bool) {
1692
- break;
1690
+ if (this.#results.has(leaves)) {
1691
+ const result = this.#results.get(leaves);
1692
+ if (result.has(node)) {
1693
+ bool = result.get(node);
1694
+ } else {
1695
+ for (const leaf of leaves) {
1696
+ bool = this._matchSelector(leaf, node, opt).has(node);
1697
+ if (!bool) {
1698
+ break;
1699
+ }
1700
+ }
1701
+ result.set(node, bool);
1702
+ this.#results.set(leaves, result);
1693
1703
  }
1704
+ } else {
1705
+ for (const leaf of leaves) {
1706
+ bool = this._matchSelector(leaf, node, opt).has(node);
1707
+ if (!bool) {
1708
+ break;
1709
+ }
1710
+ }
1711
+ const result = new WeakMap();
1712
+ result.set(node, bool);
1713
+ this.#results.set(leaves, result);
1694
1714
  }
1695
1715
  return !!bool;
1696
1716
  }
1697
1717
 
1718
+ /**
1719
+ * match HTML collection
1720
+ * @param {object} items - HTML collection
1721
+ * @param {object} opt - options
1722
+ * @returns {Set.<object>} - matched nodes
1723
+ */
1724
+ _matchHTMLCollection(items, opt = {}) {
1725
+ const { compound, filterLeaves } = opt;
1726
+ let nodes = new Set();
1727
+ const l = items.length;
1728
+ if (l) {
1729
+ if (compound) {
1730
+ for (let i = 0; i < l; i++) {
1731
+ const item = items[i];
1732
+ const bool = this._matchLeaves(filterLeaves, item, opt);
1733
+ if (bool) {
1734
+ nodes.add(item);
1735
+ }
1736
+ }
1737
+ } else {
1738
+ const arr = [].slice.call(items);
1739
+ nodes = new Set(arr);
1740
+ }
1741
+ }
1742
+ return nodes;
1743
+ }
1744
+
1698
1745
  /**
1699
1746
  * find descendant nodes
1700
1747
  * @private
@@ -1734,42 +1781,20 @@ export class Finder {
1734
1781
  }
1735
1782
  case SELECTOR_CLASS: {
1736
1783
  const items = baseNode.getElementsByClassName(leafName);
1737
- const l = items.length;
1738
- if (l) {
1739
- if (compound) {
1740
- for (let i = 0; i < l; i++) {
1741
- const item = items[i];
1742
- const bool = this._matchLeaves(filterLeaves, item, opt);
1743
- if (bool) {
1744
- nodes.add(item);
1745
- }
1746
- }
1747
- } else {
1748
- const arr = [].slice.call(items);
1749
- nodes = new Set(arr);
1750
- }
1751
- }
1784
+ nodes = this._matchHTMLCollection(items, {
1785
+ compound,
1786
+ filterLeaves
1787
+ });
1752
1788
  break;
1753
1789
  }
1754
1790
  case SELECTOR_TYPE: {
1755
1791
  if (this.#document.contentType === 'text/html' &&
1756
1792
  !/[*|]/.test(leafName)) {
1757
1793
  const items = baseNode.getElementsByTagName(leafName);
1758
- const l = items.length;
1759
- if (l) {
1760
- if (compound) {
1761
- for (let i = 0; i < l; i++) {
1762
- const item = items[i];
1763
- const bool = this._matchLeaves(filterLeaves, item, opt);
1764
- if (bool) {
1765
- nodes.add(item);
1766
- }
1767
- }
1768
- } else {
1769
- const arr = [].slice.call(items);
1770
- nodes = new Set(arr);
1771
- }
1772
- }
1794
+ nodes = this._matchHTMLCollection(items, {
1795
+ compound,
1796
+ filterLeaves
1797
+ });
1773
1798
  } else {
1774
1799
  pending = true;
1775
1800
  }
@@ -1969,6 +1994,126 @@ export class Finder {
1969
1994
  return matchedNode ?? null;
1970
1995
  }
1971
1996
 
1997
+ /**
1998
+ * match self
1999
+ * @private
2000
+ * @param {Array} leaves - AST leaves
2001
+ * @returns {Array} - [nodes, filtered]
2002
+ */
2003
+ _matchSelf(leaves) {
2004
+ const nodes = [];
2005
+ let filtered = false;
2006
+ const bool = this._matchLeaves(leaves, this.#node);
2007
+ if (bool) {
2008
+ nodes.push(this.#node);
2009
+ filtered = true;
2010
+ }
2011
+ return [nodes, filtered];
2012
+ }
2013
+
2014
+ /**
2015
+ * find lineal
2016
+ * @private
2017
+ * @param {Array} leaf - AST leaf
2018
+ * @param {object} opt - options
2019
+ * @returns {Array} - [nodes, filtered]
2020
+ */
2021
+ _findLineal(leaf, opt = {}) {
2022
+ const { complex, compound } = opt;
2023
+ const nodes = [];
2024
+ let filtered = false;
2025
+ let bool = this._matchLeaves([leaf], this.#node);
2026
+ if (bool && !complex && !compound) {
2027
+ nodes.push(this.#node);
2028
+ filtered = true;
2029
+ } else {
2030
+ if (bool) {
2031
+ nodes.push(this.#node);
2032
+ if (!compound) {
2033
+ filtered = true;
2034
+ }
2035
+ }
2036
+ let refNode = this.#node.parentNode;
2037
+ while (refNode) {
2038
+ bool = this._matchLeaves([leaf], refNode);
2039
+ if (bool) {
2040
+ nodes.push(refNode);
2041
+ if (!compound) {
2042
+ filtered = true;
2043
+ }
2044
+ }
2045
+ if (refNode.parentNode) {
2046
+ refNode = refNode.parentNode;
2047
+ } else {
2048
+ break;
2049
+ }
2050
+ }
2051
+ }
2052
+ return [nodes, filtered];
2053
+ }
2054
+
2055
+ /**
2056
+ * find first
2057
+ * @private
2058
+ * @param {Array} leaves - AST leaves
2059
+ * @returns {Array} - [nodes, filtered]
2060
+ */
2061
+ _findFirst(leaves) {
2062
+ const nodes = [];
2063
+ let filtered = false;
2064
+ const node = this._findNode(leaves, {
2065
+ node: this.#node
2066
+ });
2067
+ if (node) {
2068
+ nodes.push(node);
2069
+ filtered = true;
2070
+ }
2071
+ return [nodes, filtered];
2072
+ }
2073
+
2074
+ /**
2075
+ * find from HTML collection
2076
+ * @private
2077
+ * @param {object} items - HTML collection
2078
+ * @param {Array} nodes - nodes
2079
+ * @param {object} opt - options
2080
+ * @param {boolean} [opt.compound] - compound selector
2081
+ * @param {Array} [opt.filterLeaves] - filter leaves
2082
+ * @returns {Array} - [nodes, filtered]
2083
+ */
2084
+ _findFromHTMLCollection(items, nodes = [], opt = {}) {
2085
+ const { compound, filterLeaves } = opt;
2086
+ let filtered = false;
2087
+ const l = items.length;
2088
+ if (l) {
2089
+ if (this.#node.nodeType === ELEMENT_NODE) {
2090
+ for (let i = 0; i < l; i++) {
2091
+ const node = items[i];
2092
+ if (node === this.#node || isInclusive(node, this.#node)) {
2093
+ if (compound) {
2094
+ const bool = this._matchLeaves(filterLeaves, node, {
2095
+ warn: this.#warn
2096
+ });
2097
+ if (bool) {
2098
+ nodes.push(node);
2099
+ filtered = true;
2100
+ }
2101
+ } else {
2102
+ nodes.push(node);
2103
+ filtered = true;
2104
+ }
2105
+ }
2106
+ }
2107
+ } else {
2108
+ nodes = [].slice.call(items);
2109
+ if (!compound) {
2110
+ filtered = true;
2111
+ }
2112
+ }
2113
+ }
2114
+ return [nodes, filtered];
2115
+ }
2116
+
1972
2117
  /**
1973
2118
  * find entry nodes
1974
2119
  * @private
@@ -1986,167 +2131,105 @@ export class Finder {
1986
2131
  let nodes = [];
1987
2132
  let filtered = false;
1988
2133
  let pending = false;
1989
- if (targetType === TARGET_SELF) {
1990
- const bool = this._matchLeaves(leaves, this.#node, {
1991
- warn: this.#warn
1992
- });
1993
- if (bool) {
1994
- nodes.push(this.#node);
1995
- filtered = true;
1996
- }
1997
- } else if (targetType === TARGET_LINEAL) {
1998
- let refNode = this.#node;
1999
- while (refNode) {
2000
- const bool = this._matchLeaves(leaves, refNode, {
2134
+ switch (leafType) {
2135
+ case SELECTOR_PSEUDO_ELEMENT: {
2136
+ matchPseudoElementSelector(leafName, {
2001
2137
  warn: this.#warn
2002
2138
  });
2003
- if (bool) {
2004
- nodes.push(refNode);
2005
- filtered = true;
2006
- if (!complex) {
2007
- break;
2008
- }
2009
- }
2010
- refNode = refNode.parentNode;
2139
+ break;
2011
2140
  }
2012
- } else {
2013
- switch (leafType) {
2014
- case SELECTOR_PSEUDO_ELEMENT: {
2015
- matchPseudoElementSelector(leafName, {
2016
- warn: this.#warn
2141
+ case SELECTOR_ID: {
2142
+ if (targetType === TARGET_SELF) {
2143
+ [nodes, filtered] = this._matchSelf(leaves);
2144
+ } else if (targetType === TARGET_LINEAL) {
2145
+ [nodes, filtered] = this._findLineal(leaf, {
2146
+ complex,
2147
+ compound
2017
2148
  });
2018
- break;
2019
- }
2020
- case SELECTOR_ID: {
2021
- if (targetType === TARGET_FIRST &&
2022
- this.#root.nodeType !== ELEMENT_NODE) {
2023
- const node = this.#root.getElementById(leafName);
2024
- if (node) {
2025
- if (compound) {
2026
- const bool = this._matchLeaves(filterLeaves, node, {
2027
- warn: this.#warn
2028
- });
2029
- if (bool) {
2030
- nodes.push(node);
2031
- }
2032
- } else {
2149
+ } else if (targetType === TARGET_FIRST &&
2150
+ this.#root.nodeType !== ELEMENT_NODE) {
2151
+ const node = this.#root.getElementById(leafName);
2152
+ if (node) {
2153
+ if (compound) {
2154
+ const bool = this._matchLeaves(filterLeaves, node, {
2155
+ warn: this.#warn
2156
+ });
2157
+ if (bool) {
2033
2158
  nodes.push(node);
2159
+ filtered = true;
2034
2160
  }
2035
- filtered = true;
2036
- }
2037
- } else {
2038
- pending = true;
2039
- }
2040
- break;
2041
- }
2042
- case SELECTOR_CLASS: {
2043
- if (targetType === TARGET_FIRST) {
2044
- const node = this._findNode(leaves, {
2045
- node: this.#node
2046
- });
2047
- if (node) {
2161
+ } else {
2048
2162
  nodes.push(node);
2049
2163
  filtered = true;
2050
2164
  }
2051
- } else if (this.#root.nodeType === DOCUMENT_NODE) {
2052
- const items = this.#root.getElementsByClassName(leafName);
2053
- const l = items.length;
2054
- if (l) {
2055
- if (this.#node.nodeType === ELEMENT_NODE) {
2056
- for (let i = 0; i < l; i++) {
2057
- const node = items[i];
2058
- if (node === this.#node || isInclusive(node, this.#node)) {
2059
- if (compound) {
2060
- const bool = this._matchLeaves(filterLeaves, node, {
2061
- warn: this.#warn
2062
- });
2063
- if (bool) {
2064
- nodes.push(node);
2065
- filtered = true;
2066
- }
2067
- } else {
2068
- nodes.push(node);
2069
- filtered = true;
2070
- }
2071
- }
2072
- }
2073
- } else {
2074
- nodes = [].slice.call(items);
2075
- if (!compound) {
2076
- filtered = true;
2077
- }
2078
- }
2079
- }
2080
- } else {
2081
- pending = true;
2082
2165
  }
2083
- break;
2166
+ } else {
2167
+ pending = true;
2084
2168
  }
2085
- case SELECTOR_TYPE: {
2086
- if (targetType === TARGET_FIRST) {
2087
- const node = this._findNode(leaves, {
2088
- node: this.#node
2089
- });
2090
- if (node) {
2091
- nodes.push(node);
2092
- filtered = true;
2093
- }
2094
- } else if (this.#document.contentType === 'text/html' &&
2095
- this.#root.nodeType === DOCUMENT_NODE &&
2096
- !/[*|]/.test(leafName)) {
2097
- const items = this.#root.getElementsByTagName(leafName);
2098
- const l = items.length;
2099
- if (l) {
2100
- if (this.#node.nodeType === ELEMENT_NODE) {
2101
- for (let i = 0; i < l; i++) {
2102
- const node = items[i];
2103
- if (node === this.#node || isInclusive(node, this.#node)) {
2104
- if (compound) {
2105
- const bool = this._matchLeaves(filterLeaves, node, {
2106
- warn: this.#warn
2107
- });
2108
- if (bool) {
2109
- nodes.push(node);
2110
- filtered = true;
2111
- }
2112
- } else {
2113
- nodes.push(node);
2114
- filtered = true;
2115
- }
2116
- }
2117
- }
2118
- } else {
2119
- nodes = [].slice.call(items);
2120
- if (!compound) {
2121
- filtered = true;
2122
- }
2123
- }
2124
- }
2125
- } else {
2126
- pending = true;
2127
- }
2128
- break;
2169
+ break;
2170
+ }
2171
+ case SELECTOR_CLASS: {
2172
+ if (targetType === TARGET_SELF) {
2173
+ [nodes, filtered] = this._matchSelf(leaves);
2174
+ } else if (targetType === TARGET_LINEAL) {
2175
+ [nodes, filtered] = this._findLineal(leaf, {
2176
+ complex,
2177
+ compound
2178
+ });
2179
+ } else if (targetType === TARGET_FIRST) {
2180
+ [nodes, filtered] = this._findFirst(leaves);
2181
+ } else if (this.#root.nodeType === DOCUMENT_NODE) {
2182
+ const items = this.#root.getElementsByClassName(leafName);
2183
+ [nodes, filtered] = this._findFromHTMLCollection(items, nodes, {
2184
+ compound,
2185
+ filterLeaves
2186
+ });
2187
+ } else {
2188
+ pending = true;
2129
2189
  }
2130
- default: {
2131
- if (REG_SHADOW_HOST.test(leafName)) {
2132
- if (this.#shadow &&
2133
- this.#node.nodeType === DOCUMENT_FRAGMENT_NODE) {
2134
- const node = this._matchShadowHostPseudoClass(leaf, this.#node);
2135
- if (node) {
2136
- nodes.push(node);
2137
- }
2138
- }
2139
- } else if (targetType === TARGET_FIRST) {
2140
- const node = this._findNode(leaves, {
2141
- node: this.#node
2142
- });
2190
+ break;
2191
+ }
2192
+ case SELECTOR_TYPE: {
2193
+ if (targetType === TARGET_SELF) {
2194
+ [nodes, filtered] = this._matchSelf(leaves);
2195
+ } else if (targetType === TARGET_LINEAL) {
2196
+ [nodes, filtered] = this._findLineal(leaf, {
2197
+ complex,
2198
+ compound
2199
+ });
2200
+ } else if (this.#document.contentType === 'text/html' &&
2201
+ this.#root.nodeType === DOCUMENT_NODE &&
2202
+ !/[*|]/.test(leafName)) {
2203
+ const items = this.#root.getElementsByTagName(leafName);
2204
+ [nodes, filtered] = this._findFromHTMLCollection(items, nodes, {
2205
+ compound,
2206
+ filterLeaves
2207
+ });
2208
+ } else {
2209
+ pending = true;
2210
+ }
2211
+ break;
2212
+ }
2213
+ default: {
2214
+ if (targetType !== TARGET_LINEAL && REG_SHADOW_HOST.test(leafName)) {
2215
+ if (this.#shadow &&
2216
+ this.#node.nodeType === DOCUMENT_FRAGMENT_NODE) {
2217
+ const node = this._matchShadowHostPseudoClass(leaf, this.#node);
2143
2218
  if (node) {
2144
2219
  nodes.push(node);
2145
- filtered = true;
2146
2220
  }
2147
- } else {
2148
- pending = true;
2149
2221
  }
2222
+ } else if (targetType === TARGET_SELF) {
2223
+ [nodes, filtered] = this._matchSelf(leaves);
2224
+ } else if (targetType === TARGET_LINEAL) {
2225
+ [nodes, filtered] = this._findLineal(leaf, {
2226
+ complex,
2227
+ compound
2228
+ });
2229
+ } else if (targetType === TARGET_FIRST) {
2230
+ [nodes, filtered] = this._findFirst(leaves);
2231
+ } else {
2232
+ pending = true;
2150
2233
  }
2151
2234
  }
2152
2235
  }
@@ -2613,15 +2696,21 @@ export class Finder {
2613
2696
  * @returns {Set.<object>} - collection of matched nodes
2614
2697
  */
2615
2698
  _find(targetType, node, selector, opt) {
2616
- this._setup(node, selector, opt);
2617
- if (targetType === TARGET_ALL || targetType === TARGET_FIRST) {
2618
- this.#tree = this.#document.createTreeWalker(this.#root, WALKER_FILTER);
2619
- this.#finder = this.#document.createTreeWalker(this.#node, WALKER_FILTER);
2620
- this.#sort = false;
2621
- // TARGET_SELF, TARGET_LINEAL
2622
- } else if (node.nodeType !== ELEMENT_NODE) {
2623
- const msg = `Unexpected node ${node.nodeName}`;
2624
- throw new TypeError(msg);
2699
+ node = this._setup(node, selector, opt);
2700
+ switch (targetType) {
2701
+ case TARGET_ALL:
2702
+ case TARGET_FIRST: {
2703
+ this.#tree = this.#document.createTreeWalker(this.#root, WALKER_FILTER);
2704
+ this.#finder = this.#document.createTreeWalker(node, WALKER_FILTER);
2705
+ this.#sort = false;
2706
+ break;
2707
+ }
2708
+ default: {
2709
+ if (node.nodeType !== ELEMENT_NODE) {
2710
+ const msg = `Unexpected node ${node.nodeName}`;
2711
+ throw new TypeError(msg);
2712
+ }
2713
+ }
2625
2714
  }
2626
2715
  this._collectNodes(targetType);
2627
2716
  const nodes = this._matchNodes(targetType);
@@ -2665,7 +2754,11 @@ export class Finder {
2665
2754
  res = refNode;
2666
2755
  break;
2667
2756
  }
2668
- refNode = refNode.parentNode;
2757
+ if (refNode.parentNode) {
2758
+ refNode = refNode.parentNode;
2759
+ } else {
2760
+ break;
2761
+ }
2669
2762
  }
2670
2763
  } catch (e) {
2671
2764
  this._onError(e);
package/src/js/matcher.js CHANGED
@@ -288,6 +288,7 @@ export const _matchTypeSelector = (ast, node, opt = {}) => {
288
288
  nodeLocalName = localName;
289
289
  }
290
290
  let res;
291
+ const namespaceDeclared = isNamespaceDeclared(astPrefix, node);
291
292
  if (astPrefix === '' && nodePrefix === '') {
292
293
  if (namespaceURI === null &&
293
294
  (astLocalName === '*' || astLocalName === nodeLocalName)) {
@@ -298,7 +299,6 @@ export const _matchTypeSelector = (ast, node, opt = {}) => {
298
299
  res = node;
299
300
  }
300
301
  } else if (astPrefix && nodePrefix) {
301
- const namespaceDeclared = isNamespaceDeclared(nodePrefix, node);
302
302
  if (astPrefix === nodePrefix) {
303
303
  if (namespaceDeclared) {
304
304
  if (astLocalName === '*' || astLocalName === nodeLocalName) {
@@ -309,6 +309,9 @@ export const _matchTypeSelector = (ast, node, opt = {}) => {
309
309
  throw new DOMException(msg, SYNTAX_ERR);
310
310
  }
311
311
  }
312
+ } else if (astPrefix && !forgive && !namespaceDeclared) {
313
+ const msg = `Undeclared namespace ${astPrefix}`;
314
+ throw new DOMException(msg, SYNTAX_ERR);
312
315
  }
313
316
  return res ?? null;
314
317
  };
@@ -14,9 +14,14 @@ export class Finder {
14
14
  private _matchShadowHostPseudoClass;
15
15
  private _matchSelector;
16
16
  private _matchLeaves;
17
+ _matchHTMLCollection(items: object, opt?: object): Set<object>;
17
18
  private _findDescendantNodes;
18
19
  private _matchCombinator;
19
20
  private _findNode;
21
+ private _matchSelf;
22
+ private _findLineal;
23
+ private _findFirst;
24
+ private _findFromHTMLCollection;
20
25
  private _findEntryNodes;
21
26
  private _getEntryTwig;
22
27
  private _collectNodes;