@whitesev/domutils 1.9.2 → 1.9.3

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.
Files changed (50) hide show
  1. package/README.md +58 -58
  2. package/dist/index.amd.js +40 -22
  3. package/dist/index.amd.js.map +1 -1
  4. package/dist/index.amd.min.js +1 -1
  5. package/dist/index.amd.min.js.map +1 -1
  6. package/dist/index.cjs.js +40 -22
  7. package/dist/index.cjs.js.map +1 -1
  8. package/dist/index.cjs.min.js +1 -1
  9. package/dist/index.cjs.min.js.map +1 -1
  10. package/dist/index.esm.js +40 -22
  11. package/dist/index.esm.js.map +1 -1
  12. package/dist/index.esm.min.js +1 -1
  13. package/dist/index.esm.min.js.map +1 -1
  14. package/dist/index.iife.js +40 -22
  15. package/dist/index.iife.js.map +1 -1
  16. package/dist/index.iife.min.js +1 -1
  17. package/dist/index.iife.min.js.map +1 -1
  18. package/dist/index.system.js +40 -22
  19. package/dist/index.system.js.map +1 -1
  20. package/dist/index.system.min.js +1 -1
  21. package/dist/index.system.min.js.map +1 -1
  22. package/dist/index.umd.js +40 -22
  23. package/dist/index.umd.js.map +1 -1
  24. package/dist/index.umd.min.js +1 -1
  25. package/dist/index.umd.min.js.map +1 -1
  26. package/dist/types/src/types/DOMUtilsCSSProperty.d.ts +36 -36
  27. package/dist/types/src/types/DOMUtilsEvent.d.ts +420 -420
  28. package/dist/types/src/types/WindowApi.d.ts +14 -14
  29. package/dist/types/src/types/env.d.ts +10 -10
  30. package/dist/types/src/types/global.d.ts +1 -1
  31. package/dist/types/src/types/gm.d.ts +2 -2
  32. package/index.ts +3 -3
  33. package/package.json +21 -22
  34. package/src/CommonUtils.ts +163 -163
  35. package/src/ElementAnimate.ts +290 -290
  36. package/src/ElementEvent.ts +1569 -1569
  37. package/src/ElementHandler.ts +43 -43
  38. package/src/ElementSelector.ts +307 -289
  39. package/src/ElementWait.ts +742 -742
  40. package/src/GlobalData.ts +5 -5
  41. package/src/OriginPrototype.ts +5 -5
  42. package/src/Utils.ts +388 -388
  43. package/src/WindowApi.ts +59 -59
  44. package/src/index.ts +2052 -2052
  45. package/src/types/DOMUtilsCSSProperty.d.ts +36 -36
  46. package/src/types/DOMUtilsEvent.d.ts +420 -420
  47. package/src/types/WindowApi.d.ts +14 -14
  48. package/src/types/env.d.ts +10 -10
  49. package/src/types/global.d.ts +1 -1
  50. package/src/types/gm.d.ts +2 -2
@@ -1,289 +1,307 @@
1
- import type { WindowApiOption } from "./types/WindowApi";
2
- import { WindowApi } from "./WindowApi";
3
-
4
- class ElementSelector {
5
- windowApi: typeof WindowApi.prototype;
6
- constructor(windowApiOption?: WindowApiOption) {
7
- this.windowApi = new WindowApi(windowApiOption);
8
- }
9
- /**
10
- * 选择器,可使用以下的额外语法
11
- *
12
- *
13
- * **前置语法**
14
- *
15
- * + `xpath:` 作用:执行xpath语法,会对当前js环境下的`Document`进行节点获取
16
- *
17
- * **末尾语法**
18
- * + `:contains([text])` 作用: 找到包含指定文本内容的指定元素
19
- * + `:empty` 作用:找到既没有文本内容也没有子元素的指定元素
20
- * + `:regexp([text])` 作用: 找到符合正则表达式的内容的指定元素
21
- * @param selector 选择器
22
- * @param parent 指定父元素,默认为`document`
23
- * @example
24
- * DOMUtils.selector("div:contains('测试')")
25
- * > div.xxx
26
- * @example
27
- * DOMUtils.selector("div:empty")
28
- * > div.xxx
29
- * @example
30
- * DOMUtils.selector("div:regexp('^xxxx$')")
31
- * > div.xxx
32
- */
33
- selector<K extends keyof HTMLElementTagNameMap>(
34
- selector: K,
35
- parent?: Element | Document | DocumentFragment | ShadowRoot
36
- ): HTMLElementTagNameMap[K] | undefined;
37
- selector<E extends Element = HTMLElement>(
38
- selector: string,
39
- parent?: Element | Document | DocumentFragment | ShadowRoot
40
- ): E | undefined;
41
- selector<E extends Element = HTMLElement>(
42
- selector: string,
43
- parent?: Element | Document | DocumentFragment | ShadowRoot
44
- ) {
45
- return this.selectorAll<E>(selector, parent)[0];
46
- }
47
- /**
48
- * 选择器,可使用以下的额外语法
49
- *
50
- * **前置语法**
51
- *
52
- * + `xpath:` 作用:执行xpath语法,会对当前js环境下的`Document`进行节点获取
53
- *
54
- * **末尾语法**
55
- * + `:contains([text])` 作用: 找到包含指定文本内容的指定元素
56
- * + `:empty` 作用:找到既没有文本内容也没有子元素的指定元素
57
- * + `:regexp([text])` 作用: 找到符合正则表达式的内容的指定元素
58
- * @param selector 选择器
59
- * @param parent 指定父元素,默认为`document`
60
- * @example
61
- * DOMUtils.selectorAll("div:contains('测试')")
62
- * > [div.xxx]
63
- * @example
64
- * DOMUtils.selectorAll("div:empty")
65
- * > [div.xxx]
66
- * @example
67
- * DOMUtils.selectorAll("div:regexp('^xxxx$')")
68
- * > [div.xxx]
69
- * @example
70
- * DOMUtils.selectorAll("div:regexp(/^xxx/ig)")
71
- * > [div.xxx]
72
- */
73
- selectorAll<K extends keyof HTMLElementTagNameMap>(
74
- selector: K,
75
- parent?: Element | Document | DocumentFragment | ShadowRoot
76
- ): HTMLElementTagNameMap[K][];
77
- selectorAll<E extends Element = HTMLElement>(
78
- selector: string,
79
- parent?: Element | Document | DocumentFragment | ShadowRoot
80
- ): E[];
81
- selectorAll<E extends Element = HTMLElement>(
82
- selector: string,
83
- parent?: Element | Document | DocumentFragment | ShadowRoot
84
- ) {
85
- const context = this;
86
- parent = parent || context.windowApi.document;
87
- selector = selector.trim();
88
- if (selector.startsWith("xpath:")) {
89
- selector = selector.replace(/^xpath:/i, "");
90
- const xpathResult = context.windowApi.document.evaluate(
91
- selector,
92
- parent,
93
- null,
94
- XPathResult.ORDERED_NODE_ITERATOR_TYPE,
95
- null
96
- );
97
- const result: E[] = [];
98
- let iterateNext = xpathResult.iterateNext();
99
- while (iterateNext) {
100
- result.push(iterateNext as E);
101
- iterateNext = xpathResult.iterateNext();
102
- }
103
- return result;
104
- } else if (selector.match(/[^\s]{1}:empty$/gi)) {
105
- // empty 语法
106
- selector = selector.replace(/:empty$/gi, "");
107
- return Array.from(parent.querySelectorAll<E>(selector)).filter(($ele) => {
108
- return $ele?.innerHTML?.trim() === "";
109
- });
110
- } else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
111
- // contains 语法
112
- const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
113
- const text = textMatch![2];
114
- selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
115
- return Array.from(parent.querySelectorAll<E>(selector)).filter(($ele) => {
116
- // @ts-ignore
117
- return ($ele?.textContent || $ele?.innerText)?.includes(text);
118
- });
119
- } else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
120
- // regexp 语法
121
- const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
122
- let pattern = textMatch![2];
123
- const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
124
- let flags = "";
125
- if (flagMatch) {
126
- pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
127
- flags = flagMatch[3];
128
- }
129
- const regexp = new RegExp(pattern, flags);
130
- selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
131
- return Array.from(parent.querySelectorAll<E>(selector)).filter(($ele) => {
132
- // @ts-ignore
133
- return Boolean(($ele?.textContent || $ele?.innerText)?.match(regexp));
134
- });
135
- } else {
136
- // 普通语法
137
- return Array.from(parent.querySelectorAll<E>(selector));
138
- }
139
- }
140
- /**
141
- * 匹配元素,可使用以下的额外语法
142
- *
143
- * + :contains([text]) 作用: 找到包含指定文本内容的指定元素
144
- * + :empty 作用:找到既没有文本内容也没有子元素的指定元素
145
- * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
146
- * @param $el 元素
147
- * @param selector 选择器
148
- * @example
149
- * DOMUtils.matches("div:contains('测试')")
150
- * > true
151
- * @example
152
- * DOMUtils.matches("div:empty")
153
- * > true
154
- * @example
155
- * DOMUtils.matches("div:regexp('^xxxx$')")
156
- * > true
157
- * @example
158
- * DOMUtils.matches("div:regexp(/^xxx/ig)")
159
- * > false
160
- */
161
- matches($el: HTMLElement | Element | null | undefined, selector: string): boolean {
162
- selector = selector.trim();
163
- if ($el == null) return false;
164
- if ($el instanceof Document) return false;
165
-
166
- if (selector.match(/[^\s]{1}:empty$/gi)) {
167
- // empty 语法
168
- selector = selector.replace(/:empty$/gi, "");
169
- return $el.matches(selector) && $el?.innerHTML?.trim() === "";
170
- } else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
171
- // contains 语法
172
- const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
173
- const text = textMatch![2];
174
- selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
175
- // @ts-ignore
176
- let content = $el?.textContent || $el?.innerText;
177
- if (typeof content !== "string") {
178
- content = "";
179
- }
180
- return $el.matches(selector) && content?.includes(text);
181
- } else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
182
- // regexp 语法
183
- const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
184
- let pattern = textMatch![2];
185
- const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
186
- let flags = "";
187
- if (flagMatch) {
188
- pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
189
- flags = flagMatch[3];
190
- }
191
- const regexp = new RegExp(pattern, flags);
192
- selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
193
- // @ts-ignore
194
- let content = $el?.textContent || $el?.innerText;
195
- if (typeof content !== "string") {
196
- content = "";
197
- }
198
- return $el.matches(selector) && Boolean(content?.match(regexp));
199
- } else {
200
- // 普通语法
201
- return $el.matches(selector);
202
- }
203
- }
204
- /**
205
- * 根据选择器获取上层元素,可使用以下的额外语法
206
- *
207
- * + :contains([text]) 作用: 找到包含指定文本内容的指定元素
208
- * + :empty 作用:找到既没有文本内容也没有子元素的指定元素
209
- * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
210
- * @param $el 元素
211
- * @param selector 选择器
212
- * @example
213
- * DOMUtils.closest("div:contains('测试')")
214
- * > div.xxx
215
- * @example
216
- * DOMUtils.closest("div:empty")
217
- * > div.xxx
218
- * @example
219
- * DOMUtils.closest("div:regexp('^xxxx$')")
220
- * > div.xxxx
221
- * @example
222
- * DOMUtils.closest("div:regexp(/^xxx/ig)")
223
- * > null
224
- */
225
- closest<K extends keyof HTMLElementTagNameMap>(
226
- $el: HTMLElement | Element | null | undefined,
227
- selector: string
228
- ): HTMLElementTagNameMap[K] | null;
229
- closest<E extends Element = Element>($el: HTMLElement | Element | null | undefined, selector: string): E | null;
230
- closest<E extends Element = Element>($el: HTMLElement | Element | null | undefined, selector: string): E | null {
231
- selector = selector.trim();
232
-
233
- if ($el == null) return null;
234
- if ($el instanceof Document) return null;
235
-
236
- if (selector.match(/[^\s]{1}:empty$/gi)) {
237
- // empty 语法
238
- selector = selector.replace(/:empty$/gi, "");
239
- const $closest = $el?.closest<E>(selector);
240
- if ($closest && $closest?.innerHTML?.trim() === "") {
241
- return $closest;
242
- }
243
- return null;
244
- } else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
245
- // contains 语法
246
- const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
247
- const text = textMatch![2];
248
- selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
249
- const $closest = $el?.closest<E>(selector);
250
- if ($closest) {
251
- // @ts-ignore
252
- const content = $el?.textContent || $el?.innerText;
253
- if (typeof content === "string" && content.includes(text)) {
254
- return $closest;
255
- }
256
- }
257
- return null;
258
- } else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
259
- // regexp 语法
260
- const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
261
- let pattern = textMatch![2];
262
- const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
263
- let flags = "";
264
- if (flagMatch) {
265
- pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
266
- flags = flagMatch[3];
267
- }
268
- const regexp = new RegExp(pattern, flags);
269
- selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
270
- const $closest = $el?.closest<E>(selector);
271
- if ($closest) {
272
- // @ts-ignore
273
- const content = $el?.textContent || $el?.innerText;
274
- if (typeof content === "string" && content.match(regexp)) {
275
- return $closest;
276
- }
277
- }
278
- return null;
279
- } else {
280
- // 普通语法
281
- const $closest = $el?.closest<E>(selector);
282
- return $closest;
283
- }
284
- }
285
- }
286
-
287
- const elementSelector = new ElementSelector();
288
-
289
- export { elementSelector, ElementSelector };
1
+ import type { WindowApiOption } from "./types/WindowApi";
2
+ import { WindowApi } from "./WindowApi";
3
+
4
+ class ElementSelector {
5
+ windowApi: typeof WindowApi.prototype;
6
+ constructor(windowApiOption?: WindowApiOption) {
7
+ this.windowApi = new WindowApi(windowApiOption);
8
+ }
9
+ /**
10
+ * 选择器,可使用以下的额外语法
11
+ *
12
+ *
13
+ * **前置语法**
14
+ *
15
+ * + `xpath:` 作用:执行xpath语法,会对当前js环境下的`Document`进行节点获取
16
+ *
17
+ * **末尾语法**
18
+ * + `:contains([text])` 作用: 找到包含指定文本内容的指定元素
19
+ * + `:empty` 作用:找到既没有文本内容也没有子元素的指定元素
20
+ * + `:regexp([text])` 作用: 找到符合正则表达式的内容的指定元素
21
+ * @param selector 选择器
22
+ * @param parent 指定父元素,默认为`document`
23
+ * @example
24
+ * DOMUtils.selector("div:contains('测试')")
25
+ * > div.xxx
26
+ * @example
27
+ * DOMUtils.selector("div:empty")
28
+ * > div.xxx
29
+ * @example
30
+ * DOMUtils.selector("div:regexp('^xxxx$')")
31
+ * > div.xxx
32
+ */
33
+ selector<K extends keyof HTMLElementTagNameMap>(
34
+ selector: K,
35
+ parent?: Element | Document | DocumentFragment | ShadowRoot
36
+ ): HTMLElementTagNameMap[K] | undefined;
37
+ selector<E extends Element = HTMLElement>(
38
+ selector: string,
39
+ parent?: Element | Document | DocumentFragment | ShadowRoot
40
+ ): E | undefined;
41
+ selector<E extends Element = HTMLElement>(
42
+ selector: string,
43
+ parent?: Element | Document | DocumentFragment | ShadowRoot
44
+ ) {
45
+ return this.selectorAll<E>(selector, parent)[0];
46
+ }
47
+ /**
48
+ * 选择器,可使用以下的额外语法
49
+ *
50
+ * **前置语法**
51
+ *
52
+ * + `xpath:` 作用:执行xpath语法,会对当前js环境下的`Document`进行节点获取
53
+ *
54
+ * **末尾语法**
55
+ * + `:contains([text])` 作用: 找到包含指定文本内容的指定元素
56
+ * + `:empty` 作用:找到既没有文本内容也没有子元素的指定元素
57
+ * + `:regexp([text])` 作用: 找到符合正则表达式的内容的指定元素
58
+ * @param selector 选择器
59
+ * @param parent 指定父元素,默认为`document`
60
+ * @example
61
+ * DOMUtils.selectorAll("div:contains('测试')")
62
+ * > [div.xxx]
63
+ * @example
64
+ * DOMUtils.selectorAll("div:empty")
65
+ * > [div.xxx]
66
+ * @example
67
+ * DOMUtils.selectorAll("div:regexp('^xxxx$')")
68
+ * > [div.xxx]
69
+ * @example
70
+ * DOMUtils.selectorAll("div:regexp(/^xxx/ig)")
71
+ * > [div.xxx]
72
+ */
73
+ selectorAll<K extends keyof HTMLElementTagNameMap>(
74
+ selector: K,
75
+ parent?: Element | Document | DocumentFragment | ShadowRoot
76
+ ): HTMLElementTagNameMap[K][];
77
+ selectorAll<E extends Element = HTMLElement>(
78
+ selector: string,
79
+ parent?: Element | Document | DocumentFragment | ShadowRoot
80
+ ): E[];
81
+ selectorAll<E extends Element = HTMLElement>(
82
+ selector: string,
83
+ parent?: Element | Document | DocumentFragment | ShadowRoot
84
+ ) {
85
+ const context = this;
86
+ parent = parent || context.windowApi.document;
87
+ selector = selector.trim();
88
+ if (selector.startsWith("xpath:")) {
89
+ selector = selector.replace(/^xpath:/i, "");
90
+ const xpathResult = context.windowApi.document.evaluate(
91
+ selector,
92
+ parent,
93
+ null,
94
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE,
95
+ null
96
+ );
97
+ const result: E[] = [];
98
+ let iterateNext = xpathResult.iterateNext();
99
+ while (iterateNext) {
100
+ result.push(iterateNext as E);
101
+ iterateNext = xpathResult.iterateNext();
102
+ }
103
+ return result;
104
+ } else if (selector.match(/[^\s]{1}:empty$/gi)) {
105
+ // empty 语法
106
+ selector = selector.replace(/:empty$/gi, "");
107
+ return Array.from(parent.querySelectorAll<E>(selector)).filter(($elItem) => {
108
+ return $elItem?.innerHTML?.trim() === "";
109
+ });
110
+ } else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
111
+ // contains 语法
112
+ const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
113
+ const text = textMatch![2];
114
+ selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
115
+ return Array.from(parent.querySelectorAll<E>(selector)).filter(($elItem) => {
116
+ let domText = $elItem.textContent;
117
+ if (domText == null) {
118
+ domText = ($elItem as any as HTMLElement).innerText;
119
+ }
120
+ if (domText == null) {
121
+ return false;
122
+ }
123
+ return domText.includes(text);
124
+ });
125
+ } else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
126
+ // regexp 语法
127
+ const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
128
+ let pattern = textMatch![2];
129
+ const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
130
+ let flags = "";
131
+ if (flagMatch) {
132
+ pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
133
+ flags = flagMatch[3];
134
+ }
135
+ const regexp = new RegExp(pattern, flags);
136
+ selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
137
+ return Array.from(parent.querySelectorAll<E>(selector)).filter(($elItem) => {
138
+ let domText = $elItem.textContent;
139
+ if (domText == null) {
140
+ domText = ($elItem as any as HTMLElement).innerText;
141
+ }
142
+ if (domText == null) {
143
+ return false;
144
+ }
145
+ return !!domText.match(regexp);
146
+ });
147
+ } else {
148
+ // 普通语法
149
+ return Array.from(parent.querySelectorAll<E>(selector));
150
+ }
151
+ }
152
+ /**
153
+ * 匹配元素,可使用以下的额外语法
154
+ *
155
+ * + :contains([text]) 作用: 找到包含指定文本内容的指定元素
156
+ * + :empty 作用:找到既没有文本内容也没有子元素的指定元素
157
+ * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
158
+ * @param $el 元素
159
+ * @param selector 选择器
160
+ * @example
161
+ * DOMUtils.matches("div:contains('测试')")
162
+ * > true
163
+ * @example
164
+ * DOMUtils.matches("div:empty")
165
+ * > true
166
+ * @example
167
+ * DOMUtils.matches("div:regexp('^xxxx$')")
168
+ * > true
169
+ * @example
170
+ * DOMUtils.matches("div:regexp(/^xxx/ig)")
171
+ * > false
172
+ */
173
+ matches($el: HTMLElement | Element | null | undefined, selector: string): boolean {
174
+ selector = selector.trim();
175
+ if ($el == null) return false;
176
+ if ($el instanceof Document) return false;
177
+
178
+ if (selector.match(/[^\s]{1}:empty$/gi)) {
179
+ // empty 语法
180
+ selector = selector.replace(/:empty$/gi, "");
181
+ return $el.matches(selector) && $el?.innerHTML?.trim() === "";
182
+ } else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
183
+ // contains 语法
184
+ const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
185
+ const text = textMatch![2];
186
+ selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
187
+ let domText = $el.textContent;
188
+ if (domText == null) {
189
+ domText = ($el as any as HTMLElement).innerText;
190
+ }
191
+ if (domText == null) {
192
+ return false;
193
+ }
194
+ return $el.matches(selector) && domText.includes(text);
195
+ } else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
196
+ // regexp 语法
197
+ const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
198
+ let pattern = textMatch![2];
199
+ const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
200
+ let flags = "";
201
+ if (flagMatch) {
202
+ pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
203
+ flags = flagMatch[3];
204
+ }
205
+ const regexp = new RegExp(pattern, flags);
206
+ selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
207
+ let domText = $el.textContent;
208
+ if (domText == null) {
209
+ domText = ($el as any as HTMLElement).innerText;
210
+ }
211
+ if (domText == null) {
212
+ return false;
213
+ }
214
+ return $el.matches(selector) && !!domText.match(regexp);
215
+ } else {
216
+ // 普通语法
217
+ return $el.matches(selector);
218
+ }
219
+ }
220
+ /**
221
+ * 根据选择器获取上层元素,可使用以下的额外语法
222
+ *
223
+ * + :contains([text]) 作用: 找到包含指定文本内容的指定元素
224
+ * + :empty 作用:找到既没有文本内容也没有子元素的指定元素
225
+ * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
226
+ * @param $el 元素
227
+ * @param selector 选择器
228
+ * @example
229
+ * DOMUtils.closest("div:contains('测试')")
230
+ * > div.xxx
231
+ * @example
232
+ * DOMUtils.closest("div:empty")
233
+ * > div.xxx
234
+ * @example
235
+ * DOMUtils.closest("div:regexp('^xxxx$')")
236
+ * > div.xxxx
237
+ * @example
238
+ * DOMUtils.closest("div:regexp(/^xxx/ig)")
239
+ * > null
240
+ */
241
+ closest<K extends keyof HTMLElementTagNameMap>(
242
+ $el: HTMLElement | Element | null | undefined,
243
+ selector: string
244
+ ): HTMLElementTagNameMap[K] | null;
245
+ closest<E extends Element = Element>($el: HTMLElement | Element | null | undefined, selector: string): E | null;
246
+ closest<E extends Element = Element>($el: HTMLElement | Element | null | undefined, selector: string): E | null {
247
+ selector = selector.trim();
248
+
249
+ if ($el == null) return null;
250
+ if ($el instanceof Document) return null;
251
+
252
+ if (selector.match(/[^\s]{1}:empty$/gi)) {
253
+ // empty 语法
254
+ selector = selector.replace(/:empty$/gi, "");
255
+ const $closest = $el?.closest<E>(selector);
256
+ if ($closest && $closest?.innerHTML?.trim() === "") {
257
+ return $closest;
258
+ }
259
+ return null;
260
+ } else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
261
+ // contains 语法
262
+ const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
263
+ const text = textMatch![2];
264
+ selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
265
+ const $closest = $el?.closest<E>(selector);
266
+ if ($closest) {
267
+ // @ts-ignore
268
+ const content = $el?.textContent || $el?.innerText;
269
+ if (typeof content === "string" && content.includes(text)) {
270
+ return $closest;
271
+ }
272
+ }
273
+ return null;
274
+ } else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
275
+ // regexp 语法
276
+ const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
277
+ let pattern = textMatch![2];
278
+ const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
279
+ let flags = "";
280
+ if (flagMatch) {
281
+ pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
282
+ flags = flagMatch[3];
283
+ }
284
+ const regexp = new RegExp(pattern, flags);
285
+ selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
286
+ const $closest = $el?.closest<E>(selector);
287
+ if ($closest) {
288
+ let domText = $el.textContent;
289
+ if (domText == null) {
290
+ domText = ($el as any as HTMLElement).innerText;
291
+ }
292
+ if (typeof domText === "string" && domText.match(regexp)) {
293
+ return $closest;
294
+ }
295
+ }
296
+ return null;
297
+ } else {
298
+ // 普通语法
299
+ const $closest = $el?.closest<E>(selector);
300
+ return $closest;
301
+ }
302
+ }
303
+ }
304
+
305
+ const elementSelector = new ElementSelector();
306
+
307
+ export { elementSelector, ElementSelector };