@asamuzakjp/dom-selector 4.2.2 → 4.3.0

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/package.json CHANGED
@@ -32,15 +32,15 @@
32
32
  "@types/css-tree": "^2.3.7",
33
33
  "benchmark": "^2.1.4",
34
34
  "c8": "^9.1.0",
35
- "chai": "^5.1.0",
35
+ "chai": "^5.1.1",
36
36
  "commander": "^12.0.0",
37
- "esbuild": "^0.21.0",
37
+ "esbuild": "^0.21.1",
38
38
  "eslint": "^8.57.0",
39
39
  "eslint-config-standard": "^17.1.0",
40
40
  "eslint-plugin-import": "^2.29.1",
41
- "eslint-plugin-jsdoc": "^48.2.3",
41
+ "eslint-plugin-jsdoc": "^48.2.4",
42
42
  "eslint-plugin-regexp": "^2.5.0",
43
- "eslint-plugin-unicorn": "^52.0.0",
43
+ "eslint-plugin-unicorn": "^53.0.0",
44
44
  "happy-dom": "^14.10.1",
45
45
  "jsdom": "^24.0.0",
46
46
  "linkedom": "^0.16.11",
@@ -60,5 +60,5 @@
60
60
  "tsc": "node scripts/index clean --dir=types -i && npx tsc",
61
61
  "update-wpt": "git submodule update --init --recursive --remote"
62
62
  },
63
- "version": "4.2.2"
63
+ "version": "4.3.0"
64
64
  }
@@ -54,7 +54,7 @@ export const SHOW_ELEMENT = 1;
54
54
  export const WALKER_FILTER = 0x501;
55
55
 
56
56
  /* regexp */
57
- export const REG_LOGICAL_PSEUDO = /^(?:(?:ha|i)s|not|where)$/;
57
+ export const REG_LOGICAL_PSEUDO = /^(?:has|is|not|where)$/;
58
58
  export const REG_SHADOW_HOST = /^host(?:-context)?$/;
59
59
  export const REG_SHADOW_MODE = /^(?:close|open)$/;
60
60
  export const REG_SHADOW_PSEUDO = /^part|slotted$/;
@@ -139,9 +139,9 @@ export const getDirectionality = (node = {}) => {
139
139
  let text;
140
140
  switch (localName) {
141
141
  case 'input': {
142
- if (!node.type || /^(?:(?:butto|hidde)n|(?:emai|te|ur)l|(?:rese|submi|tex)t|password|search)$/.test(node.type)) {
142
+ if (!node.type || /^(?:button|email|hidden|password|reset|search|submit|tel|text|url)$/.test(node.type)) {
143
143
  text = node.value;
144
- } else if (/^(?:(?:ima|ran)ge|(?:dat|tim)e|c(?:olor|heckbox)|number|radio)$/.test(node.type)) {
144
+ } else if (/^(?:checkbox|color|date|image|number|radio|range|time)$/.test(node.type)) {
145
145
  res = 'ltr';
146
146
  }
147
147
  break;
@@ -164,7 +164,7 @@ export const getDirectionality = (node = {}) => {
164
164
  if (itemNodeType === TEXT_NODE) {
165
165
  text = itemTextContent.trim();
166
166
  } else if (itemNodeType === ELEMENT_NODE) {
167
- if (!/^(?:bdi|s(?:cript|tyle)|textarea)$/.test(itemLocalName) &&
167
+ if (!/^(?:bdi|script|style|textarea)$/.test(itemLocalName) &&
168
168
  (!itemDir || !regDir.test(itemDir))) {
169
169
  if (itemLocalName === 'slot') {
170
170
  text = getSlottedTextContent(item);
package/src/js/finder.js CHANGED
@@ -15,8 +15,8 @@ import {
15
15
 
16
16
  /* constants */
17
17
  import {
18
- COMBINATOR, DOCUMENT_FRAGMENT_NODE, DOCUMENT_NODE, ELEMENT_NODE, EMPTY,
19
- NOT_SUPPORTED_ERR, REG_LOGICAL_PSEUDO, REG_SHADOW_HOST, SELECTOR_CLASS,
18
+ BIT_01, COMBINATOR, DOCUMENT_FRAGMENT_NODE, DOCUMENT_NODE, ELEMENT_NODE,
19
+ EMPTY, NOT_SUPPORTED_ERR, REG_LOGICAL_PSEUDO, REG_SHADOW_HOST, SELECTOR_CLASS,
20
20
  SELECTOR_ID, SELECTOR_PSEUDO_CLASS, SELECTOR_PSEUDO_ELEMENT, SELECTOR_TYPE,
21
21
  SHOW_ALL, SYNTAX_ERR, TEXT_NODE, WALKER_FILTER
22
22
  } from './constant.js';
@@ -58,6 +58,7 @@ export class Finder {
58
58
  #content;
59
59
  #descendant;
60
60
  #document;
61
+ #event;
61
62
  #node;
62
63
  #nodes;
63
64
  #noexcept;
@@ -120,14 +121,16 @@ export class Finder {
120
121
  * @param {string} selector - CSS selector
121
122
  * @param {object} node - Document, DocumentFragment, Element node
122
123
  * @param {object} opt - options
124
+ * @param {object} [opt.event] - MouseEvent, KeyboardEvent
123
125
  * @param {boolean} [opt.noexcept] - no exception
124
126
  * @param {boolean} [opt.warn] - console warn
125
127
  * @returns {object} - node
126
128
  */
127
129
  _setup(selector, node, opt = {}) {
128
- const { noexcept, warn } = opt;
130
+ const { event, noexcept, warn } = opt;
129
131
  this.#noexcept = !!noexcept;
130
132
  this.#warn = !!warn;
133
+ this.#event = this._setEvent(event);
131
134
  this.#node = node;
132
135
  [this.#content, this.#root, this.#walker] = resolveContent(node);
133
136
  this.#shadow = isInShadowTree(node);
@@ -136,6 +139,19 @@ export class Finder {
136
139
  return node;
137
140
  }
138
141
 
142
+ /**
143
+ * set event
144
+ * @private
145
+ * @param {object} event - instance of MouseEvent, KeyboardEvent
146
+ * @returns {object} - result
147
+ */
148
+ _setEvent(event) {
149
+ return (event instanceof this.#window.MouseEvent ||
150
+ event instanceof this.#window.KeyboardEvent)
151
+ ? event
152
+ : null;
153
+ }
154
+
139
155
  /**
140
156
  * correspond ast and nodes
141
157
  * @private
@@ -898,14 +914,14 @@ export class Finder {
898
914
  } else {
899
915
  const regAnchor = /^a(?:rea)?$/;
900
916
  const regFormCtrl =
901
- /^(?:(?:fieldse|inpu|selec)t|button|opt(?:group|ion)|textarea)$/;
902
- const regFormValidity = /^(?:(?:inpu|selec)t|button|form|textarea)$/;
903
- const regInteract = /^d(?:etails|ialog)$/;
917
+ /^(?:button|fieldset|input|optgroup|option|select|textarea)$/;
918
+ const regFormValidity = /^(?:button|form|input|select|textarea)$/;
919
+ const regInteract = /^(?:details|dialog)$/;
904
920
  const regTypeCheck = /^(?:checkbox|radio)$/;
905
921
  const regTypeDate = /^(?:date(?:time-local)?|month|time|week)$/;
906
922
  const regTypeRange =
907
- /(?:(?:rang|tim)e|date(?:time-local)?|month|number|week)$/;
908
- const regTypeText = /^(?:(?:emai|te|ur)l|number|password|search|text)$/;
923
+ /(?:date(?:time-local)?|month|number|range|time|week)$/;
924
+ const regTypeText = /^(?:email|number|password|search|tel|text|url)$/;
909
925
  switch (astName) {
910
926
  case 'any-link':
911
927
  case 'link': {
@@ -928,6 +944,22 @@ export class Finder {
928
944
  // prevent fingerprinting
929
945
  break;
930
946
  }
947
+ case 'hover': {
948
+ const { target, type } = this.#event ?? {};
949
+ if ((type === 'mouseover' || type === 'pointerover') &&
950
+ node.contains(target)) {
951
+ matched.add(node);
952
+ }
953
+ break;
954
+ }
955
+ case 'active': {
956
+ const { buttons, target, type } = this.#event ?? {};
957
+ if ((type === 'mousedown' || type === 'pointerdown') &&
958
+ buttons & BIT_01 && node.contains(target)) {
959
+ matched.add(node);
960
+ }
961
+ break;
962
+ }
931
963
  case 'target': {
932
964
  const { hash } = new URL(this.#content.URL);
933
965
  if (node.id && hash === `#${node.id}` &&
@@ -961,8 +993,12 @@ export class Finder {
961
993
  }
962
994
  break;
963
995
  }
964
- case 'focus': {
965
- if (node === this.#content.activeElement && node.tabIndex >= 0) {
996
+ case 'focus':
997
+ case 'focus-visible': {
998
+ const { target, type } = this.#event ?? {};
999
+ if (node === this.#content.activeElement && node.tabIndex >= 0 &&
1000
+ (astName === 'focus' ||
1001
+ (type === 'keydown' && node.contains(target)))) {
966
1002
  let refNode = node;
967
1003
  let focus = true;
968
1004
  while (refNode) {
@@ -1527,15 +1563,13 @@ export class Finder {
1527
1563
  }
1528
1564
  break;
1529
1565
  }
1530
- case 'active':
1566
+ // not supported
1531
1567
  case 'autofill':
1532
1568
  case 'blank':
1533
1569
  case 'buffering':
1534
1570
  case 'current':
1535
- case 'focus-visible':
1536
1571
  case 'fullscreen':
1537
1572
  case 'future':
1538
- case 'hover':
1539
1573
  case 'modal':
1540
1574
  case 'muted':
1541
1575
  case 'past':
@@ -2,6 +2,7 @@ export class Finder {
2
2
  constructor(window: object);
3
3
  private _onError;
4
4
  private _setup;
5
+ private _setEvent;
5
6
  private _correspond;
6
7
  private _createTreeWalker;
7
8
  private _prepareQuerySelectorWalker;