@nodebug/browser-element-finder 1.0.8 → 1.1.5
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/README.md +215 -111
- package/index.js +417 -206
- package/index.min.js +1 -0
- package/package.json +10 -4
- package/src/element-definitions.json +4 -1
- package/src/searchable-attributes.json +0 -1
package/index.js
CHANGED
|
@@ -21,14 +21,18 @@ var ElementFinder = (() => {
|
|
|
21
21
|
var element_finder_exports = {};
|
|
22
22
|
__export(element_finder_exports, {
|
|
23
23
|
ELEMENT_DEFINITIONS: () => ELEMENT_DEFINITIONS,
|
|
24
|
-
|
|
24
|
+
findElementByAttributes: () => findElementByAttributes,
|
|
25
|
+
findElementByType: () => findElementByType,
|
|
26
|
+
findElements: () => findElements,
|
|
27
|
+
findProbableElements: () => findProbableElements,
|
|
25
28
|
getAllElements: () => getAllElements,
|
|
26
29
|
getAllFrames: () => getAllFrames,
|
|
27
30
|
getBoundingBox: () => getBoundingBox,
|
|
28
31
|
getSearchableAttributes: () => getSearchableAttributes,
|
|
32
|
+
getValidAttributes: () => getValidAttributes,
|
|
29
33
|
getValidTypes: () => getValidTypes,
|
|
30
34
|
highlight: () => highlight,
|
|
31
|
-
|
|
35
|
+
matchesAttribute: () => matchesAttribute,
|
|
32
36
|
matchesType: () => matchesType,
|
|
33
37
|
parseCondition: () => parseCondition,
|
|
34
38
|
parseXPath: () => parseXPath,
|
|
@@ -37,25 +41,6 @@ var ElementFinder = (() => {
|
|
|
37
41
|
unhighlight: () => unhighlight
|
|
38
42
|
});
|
|
39
43
|
|
|
40
|
-
// src/searchable-attributes.json
|
|
41
|
-
var searchable_attributes_default = [
|
|
42
|
-
"placeholder",
|
|
43
|
-
"value",
|
|
44
|
-
"data-test-id",
|
|
45
|
-
"data-testid",
|
|
46
|
-
"id",
|
|
47
|
-
"resource-id",
|
|
48
|
-
"name",
|
|
49
|
-
"aria-label",
|
|
50
|
-
"class",
|
|
51
|
-
"hint",
|
|
52
|
-
"title",
|
|
53
|
-
"tooltip",
|
|
54
|
-
"alt",
|
|
55
|
-
"src",
|
|
56
|
-
"aria-labelledby"
|
|
57
|
-
];
|
|
58
|
-
|
|
59
44
|
// src/element-definitions.json
|
|
60
45
|
var element_definitions_default = {
|
|
61
46
|
link: "self::a or @role='link' or @href",
|
|
@@ -65,9 +50,11 @@ var ElementFinder = (() => {
|
|
|
65
50
|
checkbox: "(self::input and @type='checkbox') or @role='checkbox'",
|
|
66
51
|
switch: "(self::input and @type='checkbox') or @role='switch' or (self::button and (contains(@class, 'switch') or @data-state))",
|
|
67
52
|
slider: "self::input[@type='range'] or @role='slider'",
|
|
53
|
+
datepicker: "self::input and @type='date'",
|
|
54
|
+
colorpicker: "self::input and @type='color'",
|
|
68
55
|
radio: "(self::input and @type='radio') or @role='radio'",
|
|
69
56
|
dropdown: "(self::select[descendant::option] or @role='combobox' or @role='listbox' or contains(@class, 'dropdown') or contains(@class, 'trigger') or ancestor::*[contains(@class, 'dropdown') or @role='combobox'])",
|
|
70
|
-
textbox: "self::textarea or (self::input and (@type='text' or @type='password' or @type='search' or @type='email')) or @role='textbox'",
|
|
57
|
+
textbox: "self::textarea or (self::input and (@type='text' or @type='password' or @type='search' or @type='email' or @type='number' or @type='tel' or @type='url')) or @role='textbox'",
|
|
71
58
|
file: "self::input and @type='file'",
|
|
72
59
|
list: "self::ul or self::ol or @role='list'",
|
|
73
60
|
listitem: "self::li or @role='listitem'",
|
|
@@ -78,10 +65,29 @@ var ElementFinder = (() => {
|
|
|
78
65
|
table: "self::table or @role='table'",
|
|
79
66
|
row: "self::tr or @role='row'",
|
|
80
67
|
column: "self::td or self::th or @role='cell' or @role='gridcell' or @role='columnheader'",
|
|
68
|
+
cell: "self::td or @role='cell' or @role='gridcell'",
|
|
81
69
|
image: "self::img or @role='img' or @alt",
|
|
82
70
|
element: "true()"
|
|
83
71
|
};
|
|
84
72
|
|
|
73
|
+
// src/searchable-attributes.json
|
|
74
|
+
var searchable_attributes_default = [
|
|
75
|
+
"placeholder",
|
|
76
|
+
"value",
|
|
77
|
+
"data-test-id",
|
|
78
|
+
"data-testid",
|
|
79
|
+
"id",
|
|
80
|
+
"resource-id",
|
|
81
|
+
"name",
|
|
82
|
+
"aria-label",
|
|
83
|
+
"hint",
|
|
84
|
+
"title",
|
|
85
|
+
"tooltip",
|
|
86
|
+
"alt",
|
|
87
|
+
"src",
|
|
88
|
+
"aria-labelledby"
|
|
89
|
+
];
|
|
90
|
+
|
|
85
91
|
// src/element-finder.js
|
|
86
92
|
var REGEX_PATTERNS = {
|
|
87
93
|
selfWithTag: /^self::([a-zA-Z0-9-]+)(?:\[([^\]]+)\])?$/,
|
|
@@ -94,29 +100,43 @@ var ElementFinder = (() => {
|
|
|
94
100
|
operatorAnd: /^\s*\band\b\s*/i
|
|
95
101
|
};
|
|
96
102
|
var MAX_RECURSION_DEPTH = 100;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
var TYPE_MATCHERS = /* @__PURE__ */ new Map();
|
|
104
|
+
for (const [type, expr] of Object.entries(element_definitions_default)) {
|
|
105
|
+
if (expr === "true()") {
|
|
106
|
+
TYPE_MATCHERS.set(type, () => true);
|
|
107
|
+
} else {
|
|
108
|
+
TYPE_MATCHERS.set(type, (el) => parseXPath(expr, el));
|
|
100
109
|
}
|
|
110
|
+
}
|
|
111
|
+
var SEARCHABLE_ATTRIBUTES = searchable_attributes_default;
|
|
112
|
+
function setSearchableAttributes(attributes) {
|
|
113
|
+
if (!Array.isArray(attributes)) {
|
|
114
|
+
throw new TypeError("attributes must be an array");
|
|
115
|
+
}
|
|
116
|
+
SEARCHABLE_ATTRIBUTES = attributes;
|
|
117
|
+
}
|
|
118
|
+
function getSearchableAttributes() {
|
|
119
|
+
return [...SEARCHABLE_ATTRIBUTES];
|
|
120
|
+
}
|
|
121
|
+
function parseXPath(expr, el, depth = 0) {
|
|
122
|
+
if (expr == null || el == null) return false;
|
|
101
123
|
if (depth > MAX_RECURSION_DEPTH) {
|
|
102
124
|
throw new Error("XPath expression exceeds maximum recursion depth");
|
|
103
125
|
}
|
|
104
126
|
expr = expr.trim();
|
|
105
127
|
if (expr === "true()") return true;
|
|
106
|
-
if (expr
|
|
107
|
-
let parenDepth =
|
|
128
|
+
if (expr[0] === "(" && expr[expr.length - 1] === ")") {
|
|
129
|
+
let parenDepth = 1;
|
|
108
130
|
let matchedAll = true;
|
|
109
|
-
for (let i =
|
|
131
|
+
for (let i = 1; i < expr.length - 1; i++) {
|
|
110
132
|
if (expr[i] === "(") parenDepth++;
|
|
111
133
|
else if (expr[i] === ")") parenDepth--;
|
|
112
|
-
if (parenDepth === 0
|
|
134
|
+
if (parenDepth === 0) {
|
|
113
135
|
matchedAll = false;
|
|
114
136
|
break;
|
|
115
137
|
}
|
|
116
138
|
}
|
|
117
|
-
if (matchedAll)
|
|
118
|
-
return parseXPath(expr.slice(1, -1), el, depth + 1);
|
|
119
|
-
}
|
|
139
|
+
if (matchedAll) return parseXPath(expr.slice(1, -1), el, depth + 1);
|
|
120
140
|
}
|
|
121
141
|
const orParts = splitByOperator(expr, "or");
|
|
122
142
|
if (orParts.length > 1) {
|
|
@@ -171,41 +191,36 @@ var ElementFinder = (() => {
|
|
|
171
191
|
return parts;
|
|
172
192
|
}
|
|
173
193
|
function parseCondition(expr, el, depth = 0) {
|
|
174
|
-
if (expr == null || el == null)
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
194
|
+
if (expr == null || el == null) return false;
|
|
177
195
|
expr = expr.trim();
|
|
178
|
-
|
|
179
|
-
if (
|
|
180
|
-
const tagName =
|
|
196
|
+
const selfMatch = expr.match(REGEX_PATTERNS.selfWithTag);
|
|
197
|
+
if (selfMatch) {
|
|
198
|
+
const tagName = selfMatch[1].toUpperCase();
|
|
181
199
|
if (el.tagName !== tagName) return false;
|
|
182
|
-
|
|
183
|
-
return parseXPath(match[2], el, depth + 1);
|
|
184
|
-
}
|
|
185
|
-
return true;
|
|
200
|
+
return selfMatch[2] ? parseXPath(selfMatch[2], el, depth + 1) : true;
|
|
186
201
|
}
|
|
187
|
-
|
|
188
|
-
if (
|
|
189
|
-
const attr = el.getAttribute(
|
|
190
|
-
return attr.toLowerCase().includes(
|
|
202
|
+
const containsMatch = expr.match(REGEX_PATTERNS.contains);
|
|
203
|
+
if (containsMatch) {
|
|
204
|
+
const attr = el.getAttribute(containsMatch[1]) || "";
|
|
205
|
+
return attr.toLowerCase().includes(containsMatch[2].toLowerCase());
|
|
191
206
|
}
|
|
192
|
-
|
|
193
|
-
if (
|
|
194
|
-
return el.getAttribute(
|
|
207
|
+
const attrEqualsMatch = expr.match(REGEX_PATTERNS.attrEquals);
|
|
208
|
+
if (attrEqualsMatch) {
|
|
209
|
+
return el.getAttribute(attrEqualsMatch[1]) === attrEqualsMatch[2];
|
|
195
210
|
}
|
|
196
|
-
|
|
197
|
-
if (
|
|
198
|
-
return el.hasAttribute(
|
|
211
|
+
const attrExistsMatch = expr.match(REGEX_PATTERNS.attrExists);
|
|
212
|
+
if (attrExistsMatch) {
|
|
213
|
+
return el.hasAttribute(attrExistsMatch[1]);
|
|
199
214
|
}
|
|
200
|
-
|
|
201
|
-
if (
|
|
202
|
-
return el.querySelector(
|
|
215
|
+
const descendantMatch = expr.match(REGEX_PATTERNS.descendant);
|
|
216
|
+
if (descendantMatch) {
|
|
217
|
+
return el.querySelector(descendantMatch[1]) !== null;
|
|
203
218
|
}
|
|
204
|
-
|
|
205
|
-
if (
|
|
219
|
+
const ancestorMatch = expr.match(REGEX_PATTERNS.ancestor);
|
|
220
|
+
if (ancestorMatch) {
|
|
206
221
|
let parent = el.parentElement;
|
|
207
222
|
while (parent) {
|
|
208
|
-
if (parseXPath(
|
|
223
|
+
if (parseXPath(ancestorMatch[1], parent, depth + 1)) return true;
|
|
209
224
|
parent = parent.parentElement;
|
|
210
225
|
}
|
|
211
226
|
return false;
|
|
@@ -213,26 +228,39 @@ var ElementFinder = (() => {
|
|
|
213
228
|
return false;
|
|
214
229
|
}
|
|
215
230
|
var ELEMENT_DEFINITIONS = Object.freeze(element_definitions_default);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
231
|
+
function getDirectText(el) {
|
|
232
|
+
let text = "";
|
|
233
|
+
for (let i = 0; i < el.childNodes.length; i++) {
|
|
234
|
+
const node = el.childNodes[i];
|
|
235
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
236
|
+
text += node.textContent;
|
|
237
|
+
}
|
|
220
238
|
}
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
function getSearchableAttributes() {
|
|
224
|
-
return [...SEARCHABLE_ATTRIBUTES];
|
|
239
|
+
return text.trim();
|
|
225
240
|
}
|
|
226
|
-
function
|
|
227
|
-
if (el
|
|
228
|
-
|
|
229
|
-
|
|
241
|
+
function isInsideStyleOrScript(el) {
|
|
242
|
+
if (el.tagName === "STYLE" || el.tagName === "SCRIPT") {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
if (el.querySelector("STYLE, SCRIPT")) {
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
let parent = el.parentElement;
|
|
249
|
+
while (parent) {
|
|
250
|
+
if (parent.tagName === "STYLE" || parent.tagName === "SCRIPT") {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
parent = parent.parentElement;
|
|
254
|
+
}
|
|
255
|
+
return false;
|
|
230
256
|
}
|
|
231
|
-
function
|
|
257
|
+
function matchesAttribute(el, value, exact = false) {
|
|
232
258
|
if (el == null) return false;
|
|
233
259
|
if (value === void 0 || value === null || value === "") return true;
|
|
234
|
-
|
|
235
|
-
|
|
260
|
+
if (isInsideStyleOrScript(el)) return false;
|
|
261
|
+
const attrs = SEARCHABLE_ATTRIBUTES;
|
|
262
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
263
|
+
const attr = attrs[i];
|
|
236
264
|
let attrValue;
|
|
237
265
|
try {
|
|
238
266
|
attrValue = el.getAttribute(attr);
|
|
@@ -240,81 +268,59 @@ var ElementFinder = (() => {
|
|
|
240
268
|
continue;
|
|
241
269
|
}
|
|
242
270
|
if (attrValue) {
|
|
243
|
-
|
|
244
|
-
if (exact ? normalized === normalizedValue : normalized.includes(normalizedValue)) {
|
|
271
|
+
if (exact ? attrValue === value : attrValue.includes(value)) {
|
|
245
272
|
return true;
|
|
246
273
|
}
|
|
247
274
|
}
|
|
248
275
|
}
|
|
249
|
-
const directText =
|
|
250
|
-
if (exact ? directText ===
|
|
276
|
+
const directText = getDirectText(el);
|
|
277
|
+
if (exact ? directText === value : directText.includes(value)) {
|
|
251
278
|
return true;
|
|
252
279
|
}
|
|
253
|
-
const textContent = el.textContent
|
|
254
|
-
if (exact ? textContent ===
|
|
280
|
+
const textContent = el.textContent;
|
|
281
|
+
if (exact ? textContent.trim() === value : textContent.includes(value)) {
|
|
255
282
|
return true;
|
|
256
283
|
}
|
|
257
|
-
if (el.tagName === "SELECT") {
|
|
258
|
-
const options = el.querySelectorAll("option");
|
|
259
|
-
for (const option of options) {
|
|
260
|
-
const optionText = option.textContent.toLowerCase().trim();
|
|
261
|
-
if (exact ? optionText === normalizedValue : optionText.includes(normalizedValue)) {
|
|
262
|
-
return true;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
284
|
return false;
|
|
267
285
|
}
|
|
268
|
-
function
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
y: rect.y,
|
|
273
|
-
width: rect.width,
|
|
274
|
-
height: rect.height,
|
|
275
|
-
top: rect.top,
|
|
276
|
-
bottom: rect.bottom,
|
|
277
|
-
left: rect.left,
|
|
278
|
-
right: rect.right,
|
|
279
|
-
midx: rect.x + rect.width / 2,
|
|
280
|
-
midy: rect.y + rect.height / 2,
|
|
281
|
-
tagName: el.tagName.toLowerCase()
|
|
282
|
-
};
|
|
286
|
+
function matchesType(el, type) {
|
|
287
|
+
if (el == null) return false;
|
|
288
|
+
const matcher = TYPE_MATCHERS.get(type);
|
|
289
|
+
return matcher ? matcher(el) : false;
|
|
283
290
|
}
|
|
284
291
|
function getAllElements(root = document) {
|
|
285
292
|
const elements = [];
|
|
293
|
+
if (root == null) return elements;
|
|
286
294
|
const rootNode = root.nodeType === Node.DOCUMENT_NODE ? root.documentElement : root;
|
|
287
295
|
if (!rootNode) return elements;
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (node2.tagName === "SCRIPT" || node2.tagName === "STYLE") {
|
|
294
|
-
return NodeFilter.FILTER_REJECT;
|
|
295
|
-
}
|
|
296
|
-
return NodeFilter.FILTER_ACCEPT;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
);
|
|
300
|
-
let node;
|
|
301
|
-
while (node = walker.nextNode()) {
|
|
296
|
+
const stack = [rootNode];
|
|
297
|
+
while (stack.length > 0) {
|
|
298
|
+
const node = stack.pop();
|
|
299
|
+
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
300
|
+
if (node.tagName === "SCRIPT" || node.tagName === "STYLE") continue;
|
|
302
301
|
elements.push(node);
|
|
302
|
+
const children = node.children;
|
|
303
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
304
|
+
stack.push(children[i]);
|
|
305
|
+
}
|
|
303
306
|
try {
|
|
304
307
|
if (node.shadowRoot) {
|
|
305
|
-
|
|
308
|
+
const shadowChildren = node.shadowRoot.children;
|
|
309
|
+
for (let i = shadowChildren.length - 1; i >= 0; i--) {
|
|
310
|
+
stack.push(shadowChildren[i]);
|
|
311
|
+
}
|
|
306
312
|
}
|
|
307
313
|
} catch (e) {
|
|
308
314
|
}
|
|
309
315
|
}
|
|
310
316
|
return elements;
|
|
311
317
|
}
|
|
312
|
-
function getAllFrames(root = window
|
|
318
|
+
function getAllFrames(root = window) {
|
|
313
319
|
const frames = [];
|
|
314
320
|
try {
|
|
315
321
|
frames.push({ window: root, document: root.document, isMainFrame: true, frameIndex: -1 });
|
|
316
322
|
const iframes = root.document.querySelectorAll("iframe");
|
|
317
|
-
for (let i = 0; i < iframes.length
|
|
323
|
+
for (let i = 0; i < iframes.length; i++) {
|
|
318
324
|
const iframe = iframes[i];
|
|
319
325
|
try {
|
|
320
326
|
if (iframe.contentWindow && iframe.contentDocument) {
|
|
@@ -339,69 +345,23 @@ var ElementFinder = (() => {
|
|
|
339
345
|
}
|
|
340
346
|
return frames;
|
|
341
347
|
}
|
|
342
|
-
function
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const headerRow = el.closest("tr");
|
|
358
|
-
if (!headerRow) continue;
|
|
359
|
-
const headerCells = Array.from(headerRow.children);
|
|
360
|
-
const colPositions = [];
|
|
361
|
-
let currentCol = 0;
|
|
362
|
-
for (const cell of headerCells) {
|
|
363
|
-
const colspan = parseInt(cell.getAttribute("colspan")) || 1;
|
|
364
|
-
colPositions.push({ cell, colStart: currentCol, colEnd: currentCol + colspan - 1 });
|
|
365
|
-
currentCol += colspan;
|
|
366
|
-
}
|
|
367
|
-
const headerInfo = colPositions.find((info) => info.cell === el);
|
|
368
|
-
if (!headerInfo) continue;
|
|
369
|
-
const allRows = table.querySelectorAll("tr");
|
|
370
|
-
for (const row of allRows) {
|
|
371
|
-
const cells = Array.from(row.children);
|
|
372
|
-
const rowColMap = /* @__PURE__ */ new Map();
|
|
373
|
-
let rowCol = 0;
|
|
374
|
-
for (const cell of cells) {
|
|
375
|
-
const colspan = parseInt(cell.getAttribute("colspan")) || 1;
|
|
376
|
-
for (let i = 0; i < colspan; i++) {
|
|
377
|
-
rowColMap.set(rowCol + i, cell);
|
|
378
|
-
}
|
|
379
|
-
rowCol += colspan;
|
|
380
|
-
}
|
|
381
|
-
for (let col = headerInfo.colStart; col <= headerInfo.colEnd; col++) {
|
|
382
|
-
const cell = rowColMap.get(col);
|
|
383
|
-
if (!cell) continue;
|
|
384
|
-
if (seenElements.has(cell)) continue;
|
|
385
|
-
if (!includeHidden) {
|
|
386
|
-
const cellWindow = ((_a = cell.ownerDocument) == null ? void 0 : _a.defaultView) || frame.window;
|
|
387
|
-
const style = cellWindow.getComputedStyle(cell);
|
|
388
|
-
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
if (cell.offsetWidth === 0 || cell.offsetHeight === 0) {
|
|
392
|
-
continue;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
expandedMatches.push({ element: cell, frame });
|
|
396
|
-
seenElements.add(cell);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return expandedMatches;
|
|
348
|
+
function getBoundingBox(el) {
|
|
349
|
+
const rect = el.getBoundingClientRect();
|
|
350
|
+
return {
|
|
351
|
+
x: rect.x,
|
|
352
|
+
y: rect.y,
|
|
353
|
+
width: rect.width,
|
|
354
|
+
height: rect.height,
|
|
355
|
+
top: rect.top,
|
|
356
|
+
bottom: rect.bottom,
|
|
357
|
+
left: rect.left,
|
|
358
|
+
right: rect.right,
|
|
359
|
+
midx: rect.x + rect.width / 2,
|
|
360
|
+
midy: rect.y + rect.height / 2,
|
|
361
|
+
tagName: el.tagName.toLowerCase()
|
|
362
|
+
};
|
|
402
363
|
}
|
|
403
|
-
function
|
|
404
|
-
var _a;
|
|
364
|
+
function findElementByType(type = "element", parent = null) {
|
|
405
365
|
if (type === null || type === void 0) {
|
|
406
366
|
type = "element";
|
|
407
367
|
}
|
|
@@ -413,22 +373,12 @@ var ElementFinder = (() => {
|
|
|
413
373
|
return { elements: [] };
|
|
414
374
|
}
|
|
415
375
|
const matches = [];
|
|
416
|
-
const frames = getAllFrames(window
|
|
376
|
+
const frames = getAllFrames(window);
|
|
417
377
|
for (const frame of frames) {
|
|
418
378
|
const allElements = getAllElements(parent || frame.document);
|
|
419
|
-
for (
|
|
379
|
+
for (let i = 0; i < allElements.length; i++) {
|
|
380
|
+
const el = allElements[i];
|
|
420
381
|
if (type && !matchesType(el, type)) continue;
|
|
421
|
-
if (text !== void 0 && !matchesContent(el, text, exact)) continue;
|
|
422
|
-
if (!includeHidden) {
|
|
423
|
-
const elWindow = ((_a = el.ownerDocument) == null ? void 0 : _a.defaultView) || frame.window;
|
|
424
|
-
const style = elWindow.getComputedStyle(el);
|
|
425
|
-
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
|
|
426
|
-
continue;
|
|
427
|
-
}
|
|
428
|
-
if (el.offsetWidth === 0 || el.offsetHeight === 0) {
|
|
429
|
-
continue;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
382
|
matches.push({ element: el, frame });
|
|
433
383
|
}
|
|
434
384
|
}
|
|
@@ -441,18 +391,274 @@ var ElementFinder = (() => {
|
|
|
441
391
|
const el = match.element;
|
|
442
392
|
if (!excludedElements.has(el)) {
|
|
443
393
|
innermostMatches.unshift(match);
|
|
444
|
-
let
|
|
445
|
-
while (
|
|
446
|
-
if (matchedElements.has(
|
|
447
|
-
excludedElements.add(
|
|
394
|
+
let parentEl = el.parentElement;
|
|
395
|
+
while (parentEl) {
|
|
396
|
+
if (matchedElements.has(parentEl)) {
|
|
397
|
+
excludedElements.add(parentEl);
|
|
448
398
|
}
|
|
449
|
-
|
|
399
|
+
parentEl = parentEl.parentElement;
|
|
450
400
|
}
|
|
451
401
|
}
|
|
452
402
|
}
|
|
453
403
|
}
|
|
454
|
-
const
|
|
455
|
-
|
|
404
|
+
const qualified = innermostMatches.map((item) => {
|
|
405
|
+
const boundingBox = getBoundingBox(item.element);
|
|
406
|
+
const tagName = item.element.tagName.toLowerCase();
|
|
407
|
+
if (!item.frame.isMainFrame) {
|
|
408
|
+
return {
|
|
409
|
+
boundingBox,
|
|
410
|
+
tagName,
|
|
411
|
+
frameIndex: item.frame.frameIndex
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
element: item.element,
|
|
416
|
+
boundingBox,
|
|
417
|
+
tagName,
|
|
418
|
+
frameIndex: item.frame.frameIndex
|
|
419
|
+
};
|
|
420
|
+
});
|
|
421
|
+
return { elements: qualified };
|
|
422
|
+
}
|
|
423
|
+
function findElementByAttributes(value, exact = false, parent = null) {
|
|
424
|
+
if (value === null || value === void 0) {
|
|
425
|
+
value = "";
|
|
426
|
+
}
|
|
427
|
+
if (typeof value !== "string") {
|
|
428
|
+
throw new TypeError(`value must be a string, got ${typeof value}`);
|
|
429
|
+
}
|
|
430
|
+
const matches = [];
|
|
431
|
+
const frames = getAllFrames(window);
|
|
432
|
+
for (const frame of frames) {
|
|
433
|
+
const allElements = getAllElements(parent || frame.document);
|
|
434
|
+
for (let i = 0; i < allElements.length; i++) {
|
|
435
|
+
const el = allElements[i];
|
|
436
|
+
if (!matchesAttribute(el, value, exact)) continue;
|
|
437
|
+
matches.push({ element: el, frame });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const filteredMatches = matches.filter((item) => {
|
|
441
|
+
const el = item.element;
|
|
442
|
+
const hasDirectMatch = hasOwnMatch(el, value, exact);
|
|
443
|
+
if (hasDirectMatch) return true;
|
|
444
|
+
for (const other of matches) {
|
|
445
|
+
if (other.element !== el && el.contains(other.element)) {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return true;
|
|
450
|
+
});
|
|
451
|
+
const qualified = filteredMatches.map((item) => {
|
|
452
|
+
const boundingBox = getBoundingBox(item.element);
|
|
453
|
+
const tagName = item.element.tagName.toLowerCase();
|
|
454
|
+
if (!item.frame.isMainFrame) {
|
|
455
|
+
return {
|
|
456
|
+
boundingBox,
|
|
457
|
+
tagName,
|
|
458
|
+
frameIndex: item.frame.frameIndex
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
return {
|
|
462
|
+
element: item.element,
|
|
463
|
+
boundingBox,
|
|
464
|
+
tagName,
|
|
465
|
+
frameIndex: item.frame.frameIndex
|
|
466
|
+
};
|
|
467
|
+
});
|
|
468
|
+
return { elements: qualified };
|
|
469
|
+
}
|
|
470
|
+
function hasOwnMatch(el, value, exact = false) {
|
|
471
|
+
if (value === void 0 || value === null || value === "") return true;
|
|
472
|
+
const attrs = SEARCHABLE_ATTRIBUTES;
|
|
473
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
474
|
+
const attr = attrs[i];
|
|
475
|
+
let attrValue;
|
|
476
|
+
try {
|
|
477
|
+
attrValue = el.getAttribute(attr);
|
|
478
|
+
} catch (e) {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (attrValue) {
|
|
482
|
+
if (exact ? attrValue === value : attrValue.includes(value)) {
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const directText = getDirectText(el);
|
|
488
|
+
if (exact ? directText === value : directText.includes(value)) {
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
function findElements(type = null, text = null, exact = false, parent = null) {
|
|
494
|
+
if (text === null || text === void 0) {
|
|
495
|
+
text = "";
|
|
496
|
+
}
|
|
497
|
+
if (type !== null && type !== void 0) {
|
|
498
|
+
if (typeof type !== "string") {
|
|
499
|
+
throw new TypeError(`type must be a string, got ${typeof type}`);
|
|
500
|
+
}
|
|
501
|
+
if (!ELEMENT_DEFINITIONS[type]) {
|
|
502
|
+
console.warn(`Unknown element type: ${type}. Valid types: ${Object.keys(ELEMENT_DEFINITIONS).join(", ")}`);
|
|
503
|
+
return { elements: [] };
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (text !== "" && typeof text !== "string") {
|
|
507
|
+
throw new TypeError(`text must be a string, got ${typeof text}`);
|
|
508
|
+
}
|
|
509
|
+
const matches = [];
|
|
510
|
+
const frames = getAllFrames(window);
|
|
511
|
+
for (const frame of frames) {
|
|
512
|
+
const allElements = getAllElements(parent || frame.document);
|
|
513
|
+
for (let i = 0; i < allElements.length; i++) {
|
|
514
|
+
const el = allElements[i];
|
|
515
|
+
if (type !== null && type !== void 0 && !matchesType(el, type)) continue;
|
|
516
|
+
if (text !== "" && !matchesAttribute(el, text, exact)) continue;
|
|
517
|
+
matches.push({ element: el, frame });
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const filteredMatches = text !== "" ? matches.filter((item) => {
|
|
521
|
+
const el = item.element;
|
|
522
|
+
const hasDirectMatch = hasOwnMatch(el, text, exact);
|
|
523
|
+
if (hasDirectMatch) return true;
|
|
524
|
+
for (const other of matches) {
|
|
525
|
+
if (other.element !== el && el.contains(other.element)) {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return true;
|
|
530
|
+
}) : matches;
|
|
531
|
+
const qualified = filteredMatches.map((item) => {
|
|
532
|
+
const boundingBox = getBoundingBox(item.element);
|
|
533
|
+
const tagName = item.element.tagName.toLowerCase();
|
|
534
|
+
if (!item.frame.isMainFrame) {
|
|
535
|
+
return {
|
|
536
|
+
boundingBox,
|
|
537
|
+
tagName,
|
|
538
|
+
frameIndex: item.frame.frameIndex
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
element: item.element,
|
|
543
|
+
boundingBox,
|
|
544
|
+
tagName,
|
|
545
|
+
frameIndex: item.frame.frameIndex
|
|
546
|
+
};
|
|
547
|
+
});
|
|
548
|
+
return { elements: qualified };
|
|
549
|
+
}
|
|
550
|
+
function getParentElement(el) {
|
|
551
|
+
if (el.parentElement) {
|
|
552
|
+
return el.parentElement;
|
|
553
|
+
}
|
|
554
|
+
try {
|
|
555
|
+
const rootNode = el.getRootNode();
|
|
556
|
+
if (rootNode && rootNode.host) {
|
|
557
|
+
return rootNode.host;
|
|
558
|
+
}
|
|
559
|
+
} catch (e) {
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
function getSiblingElements(el) {
|
|
564
|
+
const parent = getParentElement(el);
|
|
565
|
+
if (!parent) return [];
|
|
566
|
+
if (parent.shadowRoot) {
|
|
567
|
+
try {
|
|
568
|
+
return Array.from(parent.shadowRoot.children);
|
|
569
|
+
} catch (e) {
|
|
570
|
+
return [];
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return Array.from(parent.children);
|
|
574
|
+
}
|
|
575
|
+
function findNearbyElementType(el, targetType) {
|
|
576
|
+
let parent = getParentElement(el);
|
|
577
|
+
while (parent) {
|
|
578
|
+
if (matchesType(parent, targetType)) {
|
|
579
|
+
return parent;
|
|
580
|
+
}
|
|
581
|
+
parent = getParentElement(parent);
|
|
582
|
+
}
|
|
583
|
+
const immediateChildren = el.children || [];
|
|
584
|
+
for (const child of immediateChildren) {
|
|
585
|
+
if (matchesType(child, targetType)) {
|
|
586
|
+
return child;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const siblings = getSiblingElements(el);
|
|
590
|
+
for (const sibling of siblings) {
|
|
591
|
+
if (sibling !== el && matchesType(sibling, targetType)) {
|
|
592
|
+
return sibling;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
function findProbableElements(elementType, attributeText, exact = false, parent = null) {
|
|
598
|
+
const hasType = elementType !== null && elementType !== void 0 && elementType !== "";
|
|
599
|
+
const hasText = attributeText !== null && attributeText !== void 0 && attributeText !== "";
|
|
600
|
+
if (hasType && !hasText) {
|
|
601
|
+
return findElementByType(elementType, parent);
|
|
602
|
+
}
|
|
603
|
+
if (!hasType && hasText) {
|
|
604
|
+
return findElementByAttributes(attributeText, exact, parent);
|
|
605
|
+
}
|
|
606
|
+
if (hasType) {
|
|
607
|
+
if (typeof elementType !== "string") {
|
|
608
|
+
throw new TypeError(`elementType must be a string, got ${typeof elementType}`);
|
|
609
|
+
}
|
|
610
|
+
if (!ELEMENT_DEFINITIONS[elementType]) {
|
|
611
|
+
console.warn(`Unknown element type: ${elementType}. Valid types: ${Object.keys(ELEMENT_DEFINITIONS).join(", ")}`);
|
|
612
|
+
return { elements: [] };
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (hasText) {
|
|
616
|
+
if (typeof attributeText !== "string") {
|
|
617
|
+
throw new TypeError(`attributeText must be a string, got ${typeof attributeText}`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
const matches = [];
|
|
621
|
+
const frames = getAllFrames(window);
|
|
622
|
+
for (const frame of frames) {
|
|
623
|
+
const allElements = getAllElements(parent || frame.document);
|
|
624
|
+
for (let i = 0; i < allElements.length; i++) {
|
|
625
|
+
const el = allElements[i];
|
|
626
|
+
if (hasType && !matchesType(el, elementType)) continue;
|
|
627
|
+
if (hasText && !matchesAttribute(el, attributeText, exact)) continue;
|
|
628
|
+
matches.push({ element: el, frame });
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (matches.length === 0 && hasType && hasText) {
|
|
632
|
+
const attributeMatches = [];
|
|
633
|
+
for (const frame of frames) {
|
|
634
|
+
const allElements = getAllElements(parent || frame.document);
|
|
635
|
+
for (let i = 0; i < allElements.length; i++) {
|
|
636
|
+
const el = allElements[i];
|
|
637
|
+
if (!matchesAttribute(el, attributeText, exact)) continue;
|
|
638
|
+
attributeMatches.push({ element: el, frame });
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
const foundElements = /* @__PURE__ */ new Set();
|
|
642
|
+
for (const match of attributeMatches) {
|
|
643
|
+
const nearbyElement = findNearbyElementType(match.element, elementType);
|
|
644
|
+
if (nearbyElement && !foundElements.has(nearbyElement)) {
|
|
645
|
+
foundElements.add(nearbyElement);
|
|
646
|
+
matches.push({ element: nearbyElement, frame: match.frame });
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const filteredMatches = hasText ? matches.filter((item) => {
|
|
651
|
+
const el = item.element;
|
|
652
|
+
const hasDirectMatch = hasOwnMatch(el, attributeText, exact);
|
|
653
|
+
if (hasDirectMatch) return true;
|
|
654
|
+
for (const other of matches) {
|
|
655
|
+
if (other.element !== el && el.contains(other.element)) {
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return true;
|
|
660
|
+
}) : matches;
|
|
661
|
+
const qualified = filteredMatches.map((item) => {
|
|
456
662
|
const boundingBox = getBoundingBox(item.element);
|
|
457
663
|
const tagName = item.element.tagName.toLowerCase();
|
|
458
664
|
if (!item.frame.isMainFrame) {
|
|
@@ -480,7 +686,8 @@ var ElementFinder = (() => {
|
|
|
480
686
|
}
|
|
481
687
|
function highlight(elements, color = "red", width = 3) {
|
|
482
688
|
const items = extractElements(elements);
|
|
483
|
-
items.
|
|
689
|
+
for (let i = 0; i < items.length; i++) {
|
|
690
|
+
const item = items[i];
|
|
484
691
|
const el = item.element ? item.element : item;
|
|
485
692
|
if (el && el.style) {
|
|
486
693
|
el.style.outline = `${width}px solid ${color}`;
|
|
@@ -488,11 +695,12 @@ var ElementFinder = (() => {
|
|
|
488
695
|
el.style.boxShadow = `0 0 0 2px rgba(255, 255, 255, 0.8)`;
|
|
489
696
|
el.classList.add("elementfinder-highlighted");
|
|
490
697
|
}
|
|
491
|
-
}
|
|
698
|
+
}
|
|
492
699
|
}
|
|
493
700
|
function unhighlight(elements) {
|
|
494
701
|
const items = extractElements(elements);
|
|
495
|
-
items.
|
|
702
|
+
for (let i = 0; i < items.length; i++) {
|
|
703
|
+
const item = items[i];
|
|
496
704
|
const el = item.element ? item.element : item;
|
|
497
705
|
if (el && el.style) {
|
|
498
706
|
el.style.outline = "";
|
|
@@ -500,10 +708,13 @@ var ElementFinder = (() => {
|
|
|
500
708
|
el.style.boxShadow = "";
|
|
501
709
|
el.classList.remove("elementfinder-highlighted");
|
|
502
710
|
}
|
|
503
|
-
}
|
|
711
|
+
}
|
|
504
712
|
}
|
|
505
713
|
function getValidTypes() {
|
|
506
714
|
return Object.keys(ELEMENT_DEFINITIONS);
|
|
507
715
|
}
|
|
716
|
+
function getValidAttributes() {
|
|
717
|
+
return [...SEARCHABLE_ATTRIBUTES];
|
|
718
|
+
}
|
|
508
719
|
return __toCommonJS(element_finder_exports);
|
|
509
720
|
})();
|