@nodebug/browser-element-finder 1.1.8 → 1.2.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/README.md CHANGED
@@ -20,6 +20,14 @@ const results = ElementFinder.findElements('button', 'Submit')
20
20
  // Find by text only (any type)
21
21
  const results = ElementFinder.findElements(null, 'seleniumbase')
22
22
 
23
+ // Count semantic element types on the screen
24
+ const counts = ElementFinder.getElementCounts()
25
+ // Returns counts for all defined non-generic types, excluding `element`
26
+
27
+ // Count one semantic type
28
+ const buttonCount = ElementFinder.getElementCounts('button')
29
+ // Returns `{ button: 3 }`
30
+
23
31
  // Find in all frames (default)
24
32
  const results = ElementFinder.findElements('button')
25
33
 
@@ -45,6 +53,7 @@ results.elements.forEach((e) => {
45
53
  - For iframe results, switch context before interacting (see Selenium/Playwright docs)
46
54
  - Use `getValidTypes()` to enumerate all supported semantic types
47
55
  - Use `getSearchableAttributes()` to see which attributes are searched for text
56
+ - Use `getSearchableAttributeValues(element)` to inspect which searchable attributes are present on a specific element
48
57
 
49
58
  ---
50
59
 
@@ -99,6 +108,10 @@ const results = ElementFinder.findElements('button')
99
108
  const results = ElementFinder.findElements('button', 'Submit')
100
109
  // Find by text only
101
110
  const results = ElementFinder.findElements(null, 'seleniumbase')
111
+ // Count element types
112
+ const counts = ElementFinder.getElementCounts()
113
+ // Count one type
114
+ const buttonCount = ElementFinder.getElementCounts('button')
102
115
  // Check visibility of found elements
103
116
  results.elements.forEach((e) => {
104
117
  console.log('Hidden:', e.isHidden)
@@ -144,6 +157,20 @@ ElementFinder.setSearchableAttributes([
144
157
  ])
145
158
  ```
146
159
 
160
+ ### Inspecting Attribute Values
161
+
162
+ Use `getSearchableAttributeValues(element)` to inspect which current searchable attributes are present on a specific element and what values they contain.
163
+
164
+ ```js
165
+ const input = document.querySelector('input')
166
+ const values = ElementFinder.getSearchableAttributeValues(input)
167
+
168
+ console.log(values)
169
+ // { placeholder: 'Email', 'data-testid': 'email-input', id: 'email' }
170
+ ```
171
+
172
+ The returned object only includes searchable attributes that exist on the element and have non-empty values. It respects any custom attribute order set with `setSearchableAttributes()`.
173
+
147
174
  ### Pausing Animations for Screenshots
148
175
 
149
176
  When taking screenshots or performing visual assertions, animations can cause flaky tests. Use `pauseAnimations()` and `resumeAnimations()` to freeze and restore animations:
@@ -177,8 +204,8 @@ The package exports JSON files containing element type definitions and searchabl
177
204
 
178
205
  ```javascript
179
206
  // ESM - Import JSON directly
180
- import ELEMENT_DEFINITIONS from '@nodebug/browser-element-finder/element-definitions.json' assert { type: 'json' }
181
- import SEARCHABLE_ATTRIBUTES from '@nodebug/browser-element-finder/searchable-attributes.json' assert { type: 'json' }
207
+ import ELEMENT_DEFINITIONS from '@nodebug/browser-element-finder/element-definitions.json' with { type: 'json' }
208
+ import SEARCHABLE_ATTRIBUTES from '@nodebug/browser-element-finder/searchable-attributes.json' with { type: 'json' }
182
209
 
183
210
  // Get all valid element types
184
211
  console.log(Object.keys(ELEMENT_DEFINITIONS)) // ['button', 'checkbox', 'dropdown', ...]
@@ -187,38 +214,37 @@ console.log(Object.keys(ELEMENT_DEFINITIONS)) // ['button', 'checkbox', 'dropdow
187
214
  console.log(SEARCHABLE_ATTRIBUTES) // ['placeholder', 'value', 'data-test-id', ...]
188
215
  ```
189
216
 
190
- ```javascript
191
- // CommonJS - Use require
192
- const ELEMENT_DEFINITIONS = require('@nodebug/browser-element-finder/element-definitions.json')
193
- const SEARCHABLE_ATTRIBUTES = require('@nodebug/browser-element-finder/searchable-attributes.json')
194
- ```
217
+ The package is ESM-only (`"type": "module"`), so CommonJS `require()` examples are not supported.
195
218
 
196
219
  ---
197
220
 
198
221
  ## API Summary
199
222
 
200
- | Function | Description |
201
- | ------------------------------------------------- | ------------------------------------------------------------------------------ |
202
- | `findElements(type, text, exact, parent)` | Find elements by type/text, returns `{ elements: [...] }` |
203
- | `findElementsByType(type, parent)` | Find elements by type only, returns `{ elements: [...] }` |
204
- | `findElementsByAttribute(value, exact, parent)` | Find elements by text/attribute, returns `{ elements: [...] }` |
205
- | `findProbableElements(type, text, exact, parent)` | Find elements with fallback to nearby elements, returns `{ elements: [...] }` |
206
- | `highlight(elements, color, width)` | Highlight elements with outline |
207
- | `unhighlight(elements)` | Remove highlight |
208
- | `pauseAnimations()` | Pause all CSS animations and transitions, returns state object |
209
- | `resumeAnimations(state)` | Resume animations using state from `pauseAnimations()` |
210
- | `getValidTypes()` | List all supported element types |
211
- | `getValidAttributes()` | List all valid searchable attribute names |
212
- | `getBoundingBox(element)` | Get bounding box for an element |
213
- | `setSearchableAttributes(attributes)` | Set custom attributes for text search |
214
- | `getSearchableAttributes()` | Get current searchable attributes |
215
- | `matchesType(el, type)` | Check if element matches a type |
216
- | `matchesAttribute(el, value, exact)` | Check if element matches text/attribute |
217
- | `getAllElements(root)` | Get all elements (with shadow DOM) |
218
- | `getAllFrames(root)` | Get all frames (main + iframes) |
219
- | `parseXPath(expr, el, depth)` | Parse XPath-like type expressions |
220
- | `splitByOperator(expr, op)` | Split XPath by operator |
221
- | `isHidden(el)` | Check if element is hidden (display:none, visibility:hidden, hidden attribute) |
223
+ | Function | Description |
224
+ | ------------------------------------------------- | -------------------------------------------------------------------------------------------- |
225
+ | `findElements(type, text, exact, parent)` | Find elements by type/text, returns `{ elements: [...] }` |
226
+ | `findElementsByType(type, parent)` | Find elements by type only, returns `{ elements: [...] }` |
227
+ | `findElementsByAttribute(value, exact, parent)` | Find elements by text/attribute, returns `{ elements: [...] }` |
228
+ | `getElementCounts(type, parent)` | Count elements by semantic type, excluding generic `element` by default |
229
+ | `findProbableElements(type, text, exact, parent)` | Find elements with fallback to nearby elements, returns `{ elements: [...] }` |
230
+ | `highlight(elements, color, width)` | Highlight elements with outline |
231
+ | `unhighlight(elements)` | Remove highlight |
232
+ | `pauseAnimations()` | Pause all CSS animations and transitions, returns state object |
233
+ | `resumeAnimations(state)` | Resume animations using state from `pauseAnimations()` |
234
+ | `getValidTypes()` | List all supported element types |
235
+ | `getValidAttributes()` | List all valid searchable attribute names |
236
+ | `getBoundingBox(element)` | Get bounding box for an element |
237
+ | `setSearchableAttributes(attributes)` | Set custom attributes for text search |
238
+ | `getSearchableAttributes()` | Get current searchable attributes |
239
+ | `getSearchableAttributeValues(element)` | Get current non-empty searchable attribute values from an element |
240
+ | `getElementDescriptor(element)` | Get identifiable text, source attribute, occurrence index, type, and tag name for an element |
241
+ | `matchesType(el, type)` | Check if element matches a type |
242
+ | `matchesAttribute(el, value, exact)` | Check if element matches text/attribute |
243
+ | `getAllElements(root)` | Get all elements (with shadow DOM) |
244
+ | `getAllFrames(root)` | Get all frames (main + iframes) |
245
+ | `parseXPath(expr, el, depth)` | Parse XPath-like type expressions |
246
+ | `splitByOperator(expr, op)` | Split XPath by operator |
247
+ | `isHidden(el)` | Check if element is hidden (display:none, visibility:hidden, hidden attribute) |
222
248
 
223
249
  ---
224
250
 
@@ -226,12 +252,12 @@ const SEARCHABLE_ATTRIBUTES = require('@nodebug/browser-element-finder/searchabl
226
252
 
227
253
  Finds elements matching the specified type and/or text. Combines type and attribute matching in a single call. Searches all frames (main document + iframes) by default.
228
254
 
229
- | Parameter | Type | Default | Description |
230
- | --------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------- |
231
- | `type` | `string` | `null` | Element type (see supported types below). If `null`, matches any type. Throws `TypeError` for non-string values. |
232
- | `text` | `string` | `null` | Text to search for in content/attributes. If `null`/`''`/`undefined`, matches any text. |
233
- | `exact` | `boolean` | `false` | Exact text match vs substring (only used when text is provided) |
234
- | `parent` | `Element` | `null` | Parent element to search within |
255
+ | Parameter | Type | Default | Description |
256
+ | --------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |
257
+ | `type` | `string` | `null` | Element type (see supported types below). If `null` or `undefined`, matches any type. Throws `TypeError` for non-string values. |
258
+ | `text` | `string` | `null` | Text to search for in content/attributes. If `null`/`''`/`undefined`, matches any text. |
259
+ | `exact` | `boolean` | `false` | Exact text match vs substring (only used when text is provided) |
260
+ | `parent` | `Element` | `null` | Parent element to search within |
235
261
 
236
262
  **Returns**: `{ elements: [{ element, boundingBox, tagName, frameIndex, isHidden }] }`
237
263
 
@@ -332,6 +358,47 @@ Sets custom searchable attributes.
332
358
 
333
359
  Returns the current searchable attributes array.
334
360
 
361
+ ### `getElementDescriptor(element)`
362
+
363
+ Returns identifiable text for a DOM element, plus structured metadata about where it came from, its 1-based occurrence index, its semantic type, and its HTML tag name. Uniqueness can be inferred from `index`: `index === 1` means the identifiable text is unique within the current frame.
364
+
365
+ ```javascript
366
+ const descriptor = ElementFinder.getElementDescriptor(element)
367
+ ```
368
+
369
+ **Returns**:
370
+
371
+ ```javascript
372
+ {
373
+ identifiableText: 'Save', // Plain searchable text only; no CSS/XPath/index syntax
374
+ attributeName: 'title', // Attribute name, or 'text' for direct/textContent fallback
375
+ index: 2, // 1-based occurrence index; index > 1 means not unique
376
+ type: 'button', // Semantic element type, or null for non-elements
377
+ tagName: 'button' // Lowercase HTML tag name, or null for non-elements
378
+ }
379
+ ```
380
+
381
+ Descriptor selection follows the same searchable-attribute priority as text search:
382
+
383
+ 1. First non-empty searchable attribute value is used.
384
+ 2. `aria-labelledby` is resolved to referenced element text before returning the identifiable text.
385
+ 3. `src` values are returned as the image filename without path, query string, fragment, or extension.
386
+ 4. If no searchable attribute exists, direct text nodes are used and `attributeName` is set to `'text'`.
387
+ 5. If direct text is empty, trimmed full `textContent` is used and `attributeName` is set to `'text'`.
388
+ 6. If no text exists, `identifiableText` and `attributeName` are `null`, but `index`, `type`, and `tagName` are still returned.
389
+
390
+ Null or non-element input returns:
391
+
392
+ ```javascript
393
+ {
394
+ identifiableText: null,
395
+ attributeName: null,
396
+ index: 1,
397
+ type: null,
398
+ tagName: null
399
+ }
400
+ ```
401
+
335
402
  ### `matchesType(el, type)`
336
403
 
337
404
  Checks if an element matches the specified type definition.
@@ -349,6 +416,36 @@ Finds elements by type only. Searches all frames by default.
349
416
  | `type` | `string` | `"element"` | Element type (see supported types below). Throws `TypeError` for non-string values. |
350
417
  | `parent` | `Element` | `null` | Parent element to search within |
351
418
 
419
+ ### `getElementCounts(type, parent)`
420
+
421
+ Counts elements by semantic type on the current screen. Searches all frames (main document + iframes) by default.
422
+
423
+ | Parameter | Type | Default | Description |
424
+ | --------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------- |
425
+ | `type` | `string` | `null` | Specific element type to count. If `null`/`undefined`, returns counts for all defined types except `element`. |
426
+ | `parent` | `Element` | `null` | Parent element to count within |
427
+
428
+ **Returns**: `Object.<string, number>` keyed by semantic element type.
429
+
430
+ ```javascript
431
+ // Count all defined non-generic types
432
+ const counts = ElementFinder.getElementCounts()
433
+ // { button: 3, textbox: 2, link: 1, ... }
434
+
435
+ // Count one type
436
+ const buttons = ElementFinder.getElementCounts('button')
437
+ // { button: 3 }
438
+
439
+ // Count within a parent element
440
+ const inputs = ElementFinder.getElementCounts(
441
+ 'textbox',
442
+ document.querySelector('form'),
443
+ )
444
+ // { textbox: 2 }
445
+ ```
446
+
447
+ The generic `element` type is excluded from the all-types count so the result contains only semantic types such as `button`, `textbox`, `link`, and `table`.
448
+
352
449
  ### `findElementsByAttribute(value, exact, parent)`
353
450
 
354
451
  Finds elements by text/attribute value only. Searches all frames by default.
@@ -471,19 +568,19 @@ Both `column` and `cell` types find table cells, but they behave differently:
471
568
 
472
569
  ```javascript
473
570
  // Find all cells in the "City" column (header + 3 data cells = 4 total)
474
- const columnResult = ElementFinder.findElement('column', 'City')
571
+ const columnResult = ElementFinder.findElements('column', 'City')
475
572
  // Returns: [th:City, td:New York, td:London, td:Paris]
476
573
 
477
574
  // Find all cells when searching for a data cell value
478
- const columnResult2 = ElementFinder.findElement('column', 'Paris')
575
+ const columnResult2 = ElementFinder.findElements('column', 'Paris')
479
576
  // Returns: [th:City, td:New York, td:London, td:Paris]
480
577
 
481
578
  // Find only the specific cell containing "Paris"
482
- const cellResult = ElementFinder.findElement('cell', 'Paris')
579
+ const cellResult = ElementFinder.findElements('cell', 'Paris')
483
580
  // Returns: [td:Paris]
484
581
 
485
582
  // Find by header text with cell type - returns only the header cell
486
- const headerCell = ElementFinder.findElement('cell', 'City')
583
+ const headerCell = ElementFinder.findElements('cell', 'City')
487
584
  // Returns: [] (no td elements match "City" header text)
488
585
  ```
489
586
 
package/index.js CHANGED
@@ -28,6 +28,9 @@ var ElementFinder = (() => {
28
28
  getAllElements: () => getAllElements,
29
29
  getAllFrames: () => getAllFrames,
30
30
  getBoundingBox: () => getBoundingBox,
31
+ getElementCounts: () => getElementCounts,
32
+ getElementDescriptor: () => getElementDescriptor,
33
+ getSearchableAttributeValues: () => getSearchableAttributeValues,
31
34
  getSearchableAttributes: () => getSearchableAttributes,
32
35
  getValidAttributes: () => getValidAttributes,
33
36
  getValidTypes: () => getValidTypes,
@@ -122,6 +125,156 @@ var ElementFinder = (() => {
122
125
  function getSearchableAttributes() {
123
126
  return [...SEARCHABLE_ATTRIBUTES];
124
127
  }
128
+ function getSearchableAttributeValues(el) {
129
+ if (el == null || el.nodeType !== Node.ELEMENT_NODE) return {};
130
+ const values = {};
131
+ const attrs = SEARCHABLE_ATTRIBUTES;
132
+ for (let i = 0; i < attrs.length; i++) {
133
+ const attr = attrs[i];
134
+ let attrValue;
135
+ try {
136
+ attrValue = el.getAttribute(attr);
137
+ } catch (e) {
138
+ continue;
139
+ }
140
+ if (attrValue !== null && attrValue !== void 0 && attrValue !== "") {
141
+ values[attr] = attrValue;
142
+ }
143
+ }
144
+ return values;
145
+ }
146
+ function normalizeDescriptorText(text) {
147
+ if (text == null) return "";
148
+ return String(text).replace(/\s+/g, " ").trim();
149
+ }
150
+ function getImageFilenameWithoutExtension(src) {
151
+ const normalizedSrc = normalizeDescriptorText(src);
152
+ if (!normalizedSrc) return "";
153
+ const withoutQueryOrFragment = normalizedSrc.split(/[?#]/)[0];
154
+ const lastSlashIndex = Math.max(
155
+ withoutQueryOrFragment.lastIndexOf("/"),
156
+ withoutQueryOrFragment.lastIndexOf("\\")
157
+ );
158
+ const filenameWithExtension = lastSlashIndex >= 0 ? withoutQueryOrFragment.slice(lastSlashIndex + 1) : withoutQueryOrFragment;
159
+ const lastDotIndex = filenameWithExtension.lastIndexOf(".");
160
+ return lastDotIndex > 0 ? filenameWithExtension.slice(0, lastDotIndex) : filenameWithExtension;
161
+ }
162
+ function getResolvedAriaLabelledByText(el) {
163
+ const labelledBy = el.getAttribute("aria-labelledby");
164
+ if (!labelledBy) return "";
165
+ const ids = labelledBy.split(/\s+/);
166
+ const ownerDocument = el.ownerDocument || document;
167
+ let text = "";
168
+ for (const id of ids) {
169
+ try {
170
+ const refEl = ownerDocument.getElementById(id);
171
+ if (!refEl) continue;
172
+ const refText = normalizeDescriptorText(refEl.textContent);
173
+ if (refText) {
174
+ text = text ? `${text} ${refText}` : refText;
175
+ }
176
+ } catch (e) {
177
+ }
178
+ }
179
+ return text;
180
+ }
181
+ function getElementDescriptorText(el) {
182
+ const values = getSearchableAttributeValues(el);
183
+ const attrs = SEARCHABLE_ATTRIBUTES;
184
+ for (let i = 0; i < attrs.length; i++) {
185
+ const attr = attrs[i];
186
+ if (!Object.prototype.hasOwnProperty.call(values, attr)) continue;
187
+ const rawText = attr === "aria-labelledby" ? getResolvedAriaLabelledByText(el) : attr === "src" ? getImageFilenameWithoutExtension(values[attr]) : values[attr];
188
+ const identifiableText = normalizeDescriptorText(rawText);
189
+ if (identifiableText) {
190
+ return { attributeName: attr, identifiableText };
191
+ }
192
+ }
193
+ const directText = normalizeDescriptorText(getDirectText(el));
194
+ if (directText) {
195
+ return { attributeName: "text", identifiableText: directText };
196
+ }
197
+ const fullText = normalizeDescriptorText(el.textContent);
198
+ if (fullText) {
199
+ return { attributeName: "text", identifiableText: fullText };
200
+ }
201
+ return null;
202
+ }
203
+ function getElementDescriptorFrame(el) {
204
+ if (!el || !el.ownerDocument) return null;
205
+ try {
206
+ const frames = getAllFrames(window);
207
+ for (let i = 0; i < frames.length; i++) {
208
+ if (frames[i].document === el.ownerDocument) {
209
+ return frames[i].document;
210
+ }
211
+ }
212
+ } catch (e) {
213
+ }
214
+ return el.ownerDocument;
215
+ }
216
+ function getElementDescriptorUniqueness(el, text) {
217
+ const root = getElementDescriptorFrame(el);
218
+ if (!root) {
219
+ return { index: 1 };
220
+ }
221
+ const elements = getAllElements(root);
222
+ let index = 1;
223
+ let count = 0;
224
+ for (let i = 0; i < elements.length; i++) {
225
+ const candidate = elements[i];
226
+ if (candidate !== el && (candidate === root.documentElement || candidate.tagName === "BODY")) {
227
+ continue;
228
+ }
229
+ const candidateDescriptor = getElementDescriptorText(candidate);
230
+ if (!candidateDescriptor || candidateDescriptor.identifiableText !== text) continue;
231
+ count++;
232
+ if (candidate === el) {
233
+ index = count;
234
+ }
235
+ }
236
+ return { index };
237
+ }
238
+ function getElementDescriptorType(el) {
239
+ if (el == null || el.nodeType !== Node.ELEMENT_NODE) return null;
240
+ const types = Object.keys(ELEMENT_DEFINITIONS);
241
+ for (let i = 0; i < types.length; i++) {
242
+ const type = types[i];
243
+ if (type === "element") continue;
244
+ if (matchesType(el, type)) return type;
245
+ }
246
+ return "element";
247
+ }
248
+ function getElementDescriptor(el) {
249
+ if (el == null || el.nodeType !== Node.ELEMENT_NODE) {
250
+ return {
251
+ identifiableText: null,
252
+ attributeName: null,
253
+ index: 1,
254
+ type: null,
255
+ tagName: null
256
+ };
257
+ }
258
+ const type = getElementDescriptorType(el);
259
+ const descriptorSource = getElementDescriptorText(el);
260
+ if (!descriptorSource) {
261
+ return {
262
+ identifiableText: null,
263
+ attributeName: null,
264
+ index: 1,
265
+ type,
266
+ tagName: el.tagName.toLowerCase()
267
+ };
268
+ }
269
+ const uniqueness = getElementDescriptorUniqueness(el, descriptorSource.identifiableText);
270
+ return {
271
+ identifiableText: descriptorSource.identifiableText,
272
+ attributeName: descriptorSource.attributeName,
273
+ index: uniqueness.index,
274
+ type,
275
+ tagName: el.tagName.toLowerCase()
276
+ };
277
+ }
125
278
  function parseXPath(expr, el, depth = 0) {
126
279
  if (expr == null || el == null) return false;
127
280
  if (depth > MAX_RECURSION_DEPTH) {
@@ -411,15 +564,20 @@ var ElementFinder = (() => {
411
564
  }
412
565
  return false;
413
566
  }
414
- function findElementsByType(type = "element", parent = null) {
567
+ function findElementsByType(type = "element", parent = null, options = null) {
415
568
  if (type === null || type === void 0) {
416
569
  type = "element";
417
570
  }
418
571
  if (typeof type !== "string") {
419
572
  throw new TypeError(`type must be a string, got ${typeof type}`);
420
573
  }
574
+ const failOnUnknownType = options && options.failOnUnknownType === true;
421
575
  if (type && !ELEMENT_DEFINITIONS[type]) {
422
- console.warn(`Unknown element type: ${type}. Valid types: ${Object.keys(ELEMENT_DEFINITIONS).join(", ")}`);
576
+ const message = `Unknown element type: ${type}. Valid types: ${Object.keys(ELEMENT_DEFINITIONS).join(", ")}`;
577
+ if (failOnUnknownType) {
578
+ throw new TypeError(`Unknown element type: ${type}`);
579
+ }
580
+ console.warn(message);
423
581
  return { elements: [] };
424
582
  }
425
583
  const matches = [];
@@ -556,7 +714,43 @@ var ElementFinder = (() => {
556
714
  }
557
715
  return false;
558
716
  }
559
- function findElements(type = null, text = null, exact = false, parent = null) {
717
+ function getElementCounts(type = null, parent = null, options = null) {
718
+ const hasType = type !== null && type !== void 0;
719
+ if (hasType) {
720
+ if (typeof type !== "string") {
721
+ throw new TypeError(`type must be a string, got ${typeof type}`);
722
+ }
723
+ if (!ELEMENT_DEFINITIONS[type]) {
724
+ const message = `Unknown element type: ${type}. Valid types: ${Object.keys(ELEMENT_DEFINITIONS).join(", ")}`;
725
+ if (options && options.failOnUnknownType === true) {
726
+ throw new TypeError(`Unknown element type: ${type}`);
727
+ }
728
+ console.warn(message);
729
+ return { [type]: 0 };
730
+ }
731
+ }
732
+ const counts = {};
733
+ const targetTypes = hasType ? [type] : Object.keys(ELEMENT_DEFINITIONS).filter((item) => item !== "element");
734
+ for (let i = 0; i < targetTypes.length; i++) {
735
+ counts[targetTypes[i]] = 0;
736
+ }
737
+ const frames = getAllFrames(window);
738
+ for (let i = 0; i < frames.length; i++) {
739
+ const frame = frames[i];
740
+ const allElements = getAllElements(parent || frame.document);
741
+ for (let j = 0; j < allElements.length; j++) {
742
+ const el = allElements[j];
743
+ for (let k = 0; k < targetTypes.length; k++) {
744
+ const targetType = targetTypes[k];
745
+ if (matchesType(el, targetType)) {
746
+ counts[targetType] += 1;
747
+ }
748
+ }
749
+ }
750
+ }
751
+ return counts;
752
+ }
753
+ function findElements(type = null, text = null, exact = false, parent = null, options = null) {
560
754
  if (text === null || text === void 0) {
561
755
  text = "";
562
756
  }
@@ -565,7 +759,11 @@ var ElementFinder = (() => {
565
759
  throw new TypeError(`type must be a string, got ${typeof type}`);
566
760
  }
567
761
  if (!ELEMENT_DEFINITIONS[type]) {
568
- console.warn(`Unknown element type: ${type}. Valid types: ${Object.keys(ELEMENT_DEFINITIONS).join(", ")}`);
762
+ const message = `Unknown element type: ${type}. Valid types: ${Object.keys(ELEMENT_DEFINITIONS).join(", ")}`;
763
+ if (options && options.failOnUnknownType === true) {
764
+ throw new TypeError(`Unknown element type: ${type}`);
765
+ }
766
+ console.warn(message);
569
767
  return { elements: [] };
570
768
  }
571
769
  }
@@ -690,11 +888,11 @@ var ElementFinder = (() => {
690
888
  }
691
889
  return null;
692
890
  }
693
- function findProbableElements(elementType, attributeText, exact = false, parent = null) {
891
+ function findProbableElements(elementType, attributeText, exact = false, parent = null, options = null) {
694
892
  const hasType = elementType !== null && elementType !== void 0 && elementType !== "";
695
893
  const hasText = attributeText !== null && attributeText !== void 0 && attributeText !== "";
696
894
  if (hasType && !hasText) {
697
- return findElementsByType(elementType, parent);
895
+ return findElementsByType(elementType, parent, options);
698
896
  }
699
897
  if (!hasType && hasText) {
700
898
  return findElementsByAttribute(attributeText, exact, parent);
@@ -704,7 +902,11 @@ var ElementFinder = (() => {
704
902
  throw new TypeError(`elementType must be a string, got ${typeof elementType}`);
705
903
  }
706
904
  if (!ELEMENT_DEFINITIONS[elementType]) {
707
- console.warn(`Unknown element type: ${elementType}. Valid types: ${Object.keys(ELEMENT_DEFINITIONS).join(", ")}`);
905
+ const message = `Unknown element type: ${elementType}. Valid types: ${Object.keys(ELEMENT_DEFINITIONS).join(", ")}`;
906
+ if (options && options.failOnUnknownType === true) {
907
+ throw new TypeError(`Unknown element type: ${elementType}`);
908
+ }
909
+ console.warn(message);
708
910
  return { elements: [] };
709
911
  }
710
912
  }
package/index.min.js CHANGED
@@ -1,4 +1,4 @@
1
- var ElementFinder=(()=>{var I=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var U=Object.prototype.hasOwnProperty;var V=(e,t)=>{for(var n in t)I(e,n,{get:t[n],enumerable:!0})},W=(e,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of z(t))!U.call(e,r)&&r!==n&&I(e,r,{get:()=>t[r],enumerable:!(i=F(t,r))||i.enumerable});return e};var X=e=>W(I({},"__esModule",{value:!0}),e);var le={};V(le,{ELEMENT_DEFINITIONS:()=>y,findElements:()=>v,findElementsByAttribute:()=>j,findElementsByType:()=>H,findProbableElements:()=>te,getAllElements:()=>g,getAllFrames:()=>A,getBoundingBox:()=>S,getSearchableAttributes:()=>J,getValidAttributes:()=>ae,getValidTypes:()=>se,highlight:()=>ne,isHidden:()=>N,matchesAttribute:()=>E,matchesType:()=>h,parseCondition:()=>R,parseXPath:()=>b,pauseAnimations:()=>oe,resumeAnimations:()=>ie,setSearchableAttributes:()=>Q,splitByOperator:()=>B,unhighlight:()=>re});var T={link:"self::a or @role='link' or @href",navigation:"@role='navigation' or self::nav",heading:"@role='heading' or self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6",button:"self::button or @role='button' or @type='button' or @type='submit'",checkbox:"(self::input and @type='checkbox') or @role='checkbox'",switch:"(self::input and @type='checkbox') or @role='switch' or (self::button and (contains(@class, 'switch') or @data-state))",slider:"self::input[@type='range'] or @role='slider'",datepicker:"self::input[@type='date'] or @role='date'",colorpicker:"self::input[@type='color'] or @role='color'",radio:"(self::input and @type='radio') or @role='radio'",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'])",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'",file:"self::input and @type='file'",list:"self::ul or self::ol or @role='list'",listitem:"self::li or @role='listitem'",menu:"self::menu or @role='menu'",menuitem:"@role='menuitem'",toolbar:"@role='toolbar'",dialog:"@role='dialog' or @role='alertdialog'",table:"self::table or @role='table'",row:"self::tr or @role='row'",column:"self::td or self::th or @role='cell' or @role='gridcell' or @role='columnheader'",cell:"self::td or @role='cell' or @role='gridcell'",image:"self::img or @role='img' or @alt",element:"true()"};var D=["placeholder","value","data-value","data-test-id","data-testid","id","resource-id","name","aria-label","hint","title","tooltip","alt","src","aria-labelledby"];var p={selfWithTag:/^self::([a-zA-Z0-9-]+)(?:\[([^\]]+)\])?$/,contains:/contains\(@([a-zA-Z0-9-]+),\s*['"]([^'"]+)['"]\)/i,attrEquals:/@([a-zA-Z0-9-]+)\s*=\s*['"]([^'"]*)['"]/,attrExists:/^@([a-zA-Z0-9-]+)$/,descendant:/descendant::([a-zA-Z0-9-]+)/i,ancestor:/ancestor::\*\[([^\]]+)\]/i,operatorOr:/^\s*\bor\b\s*/i,operatorAnd:/^\s*\band\b\s*/i},G=100,C=new Map;for(let[e,t]of Object.entries(T))t==="true()"?C.set(e,()=>!0):C.set(e,n=>b(t,n));var x=D;function Q(e){if(!Array.isArray(e))throw new TypeError("attributes must be an array");x=e}function J(){return[...x]}function b(e,t,n=0){if(e==null||t==null)return!1;if(n>G)throw new Error("XPath expression exceeds maximum recursion depth");if(e=e.trim(),e==="true()")return!0;if(e[0]==="("&&e[e.length-1]===")"){let l=1,s=!0;for(let a=1;a<e.length-1;a++)if(e[a]==="("?l++:e[a]===")"&&l--,l===0){s=!1;break}if(s)return b(e.slice(1,-1),t,n+1)}let i=B(e,"or");if(i.length>1){for(let l of i)if(b(l,t,n+1))return!0;return!1}let r=B(e,"and");if(r.length>1){for(let l of r)if(!b(l,t,n+1))return!1;return!0}return R(e,t,n)}function B(e,t){let n=[],i=0,r="",l=!1,s="",a=t==="or"?p.operatorOr:p.operatorAnd;for(let o=0;o<e.length;o++){let f=e[o];if((f==="'"||f==='"')&&(o===0||e[o-1]!=="\\")&&(l?f===s&&(l=!1):(l=!0,s=f)),!l&&(f==="("?i++:f===")"&&i--,i===0)){let u=e.slice(o).match(a);if(u){n.push(r.trim()),o+=u[0].length-1,r="";continue}}r+=f}return r.trim()&&n.push(r.trim()),n}function R(e,t,n=0){if(e==null||t==null)return!1;e=e.trim();let i=e.match(p.selfWithTag);if(i){let f=i[1].toUpperCase();return t.tagName!==f?!1:i[2]?b(i[2],t,n+1):!0}let r=e.match(p.contains);if(r)return(t.getAttribute(r[1])||"").toLowerCase().includes(r[2].toLowerCase());let l=e.match(p.attrEquals);if(l)return t.getAttribute(l[1])===l[2];let s=e.match(p.attrExists);if(s)return t.hasAttribute(s[1]);let a=e.match(p.descendant);if(a)return t.querySelector(a[1])!==null;let o=e.match(p.ancestor);if(o){let f=t.parentElement;for(;f;){if(b(o[1],f,n+1))return!0;f=f.parentElement}return!1}return!1}var y=Object.freeze(T);function $(e){let t="";for(let n=0;n<e.childNodes.length;n++){let i=e.childNodes[n];i.nodeType===Node.TEXT_NODE&&(t+=i.textContent)}return t.trim()}function K(e){if(e.tagName==="STYLE"||e.tagName==="SCRIPT"||e.querySelector("STYLE, SCRIPT"))return!0;let t=e.parentElement;for(;t;){if(t.tagName==="STYLE"||t.tagName==="SCRIPT")return!0;t=t.parentElement}return!1}function q(e){let t=e.getAttribute("aria-labelledby");if(!t)return"";let n=t.split(/\s+/),i="";for(let r of n)try{let l=document.getElementById(r);l&&(i+=l.textContent)}catch(l){}return i}function E(e,t,n=!1){if(e==null)return!1;if(t==null||t==="")return!0;if(K(e))return!1;let i=x;for(let s=0;s<i.length;s++){let a=i[s],o;try{o=e.getAttribute(a)}catch(f){continue}if(o){if(a==="aria-labelledby"){if(n?o===t:o.includes(t))return!0;let f=q(e);if(f&&(n?f===t:f.includes(t)))return!0}else if(n?o===t:o.includes(t))return!0}}let r=$(e);if(n?r===t:r.includes(t))return!0;let l=e.textContent;return!!(n?l.trim()===t:l.includes(t))}function h(e,t){if(e==null)return!1;let n=C.get(t);return n?n(e):!1}function g(e=document){let t=[];if(e==null)return t;let n=e.nodeType===Node.DOCUMENT_NODE?e.documentElement:e;if(!n)return t;let i=[n];for(;i.length>0;){let r=i.pop();if(r.nodeType!==Node.ELEMENT_NODE||r.tagName==="SCRIPT"||r.tagName==="STYLE")continue;t.push(r);let l=r.children;for(let s=l.length-1;s>=0;s--)i.push(l[s]);try{if(r.shadowRoot){let s=r.shadowRoot.children;for(let a=s.length-1;a>=0;a--)i.push(s[a])}}catch(s){}}return t}function A(e=window){let t=[];try{t.push({window:e,document:e.document,isMainFrame:!0,frameIndex:-1});let n=e.document.querySelectorAll("iframe");for(let i=0;i<n.length;i++){let r=n[i];try{r.contentWindow&&r.contentDocument&&t.push({window:r.contentWindow,document:r.contentDocument,isMainFrame:!1,frameElement:r,frameIndex:i})}catch(l){l.name==="SecurityError"?console.warn("Skipping cross-origin iframe:",l.message):console.warn("Error accessing iframe:",l.message)}}}catch(n){console.warn("Error getting frames:",n.message)}return t}function S(e){let t=e.getBoundingClientRect();return{x:t.x,y:t.y,width:t.width,height:t.height,top:t.top,bottom:t.bottom,left:t.left,right:t.right,midx:t.x+t.width/2,midy:t.y+t.height/2,tagName:e.tagName.toLowerCase()}}function N(e){if(e==null||e.offsetWidth===0&&e.offsetHeight===0)return!0;try{let t=window.getComputedStyle(e);if(t.visibility==="hidden"||t.visibility==="collapse"||t.display==="none")return!0}catch(t){}return!!e.hasAttribute("hidden")}function H(e="element",t=null){if(e==null&&(e="element"),typeof e!="string")throw new TypeError(`type must be a string, got ${typeof e}`);if(e&&!y[e])return console.warn(`Unknown element type: ${e}. Valid types: ${Object.keys(y).join(", ")}`),{elements:[]};let n=[],i=A(window);for(let s of i){let a=g(t||s.document);for(let o=0;o<a.length;o++){let f=a[o];e&&!h(f,e)||n.push({element:f,frame:s})}}let r=[];if(n.length>0){let s=new Set(n.map(o=>o.element)),a=new Set;for(let o=n.length-1;o>=0;o--){let f=n[o],c=f.element;if(!a.has(c)){r.unshift(f);let u=c.parentElement;for(;u;)s.has(u)&&a.add(u),u=u.parentElement}}}return{elements:r.map(s=>{let a=S(s.element),o=s.element.tagName.toLowerCase(),f=N(s.element);return s.frame.isMainFrame?{element:s.element,boundingBox:a,tagName:o,frameIndex:s.frame.frameIndex,isHidden:f}:{boundingBox:a,tagName:o,frameIndex:s.frame.frameIndex,isHidden:f}})}}function j(e,t=!1,n=null){if(e==null&&(e=""),typeof e!="string")throw new TypeError(`value must be a string, got ${typeof e}`);let i=[],r=A(window);for(let a of r){let o=g(n||a.document);for(let f=0;f<o.length;f++){let c=o[f];E(c,e,t)&&i.push({element:c,frame:a})}}return{elements:i.filter(a=>{let o=a.element;if(P(o,e,t))return!0;for(let c of i)if(c.element!==o&&o.contains(c.element))return!1;return!0}).map(a=>{let o=S(a.element),f=a.element.tagName.toLowerCase(),c=N(a.element);return a.frame.isMainFrame?{element:a.element,boundingBox:o,tagName:f,frameIndex:a.frame.frameIndex,isHidden:c}:{boundingBox:o,tagName:f,frameIndex:a.frame.frameIndex,isHidden:c}})}}function P(e,t,n=!1){if(t==null||t==="")return!0;let i=x;for(let l=0;l<i.length;l++){let s=i[l],a;try{a=e.getAttribute(s)}catch(o){continue}if(a){if(s==="aria-labelledby"){if(n?a===t:a.includes(t))return!0;let o=q(e);if(o&&(n?o===t:o.includes(t)))return!0}else if(n?a===t:a.includes(t))return!0}}let r=$(e);return!!(n?r===t:r.includes(t))}function v(e=null,t=null,n=!1,i=null){if(t==null&&(t=""),e!=null){if(typeof e!="string")throw new TypeError(`type must be a string, got ${typeof e}`);if(!y[e])return console.warn(`Unknown element type: ${e}. Valid types: ${Object.keys(y).join(", ")}`),{elements:[]}}if(t!==""&&typeof t!="string")throw new TypeError(`text must be a string, got ${typeof t}`);let r=[],l=A(window);for(let o of l){let f=g(i||o.document);for(let c=0;c<f.length;c++){let u=f[c];e!=null&&!h(u,e)||t!==""&&!E(u,t,n)||r.push({element:u,frame:o})}}return{elements:(t!==""?r.filter(o=>{let f=o.element;if(P(f,t,n))return!0;for(let u of r)if(u.element!==f&&f.contains(u.element))return!1;return!0}):r).map(o=>{let f=S(o.element),c=o.element.tagName.toLowerCase(),u=N(o.element);return o.frame.isMainFrame?{element:o.element,boundingBox:f,tagName:c,frameIndex:o.frame.frameIndex,isHidden:u}:{boundingBox:f,tagName:c,frameIndex:o.frame.frameIndex,isHidden:u}})}}function O(e){if(e.parentElement)return e.parentElement;try{let t=e.getRootNode();if(t&&t.host)return t.host}catch(t){}return null}function L(e){let t=O(e);if(!t)return[];if(t.shadowRoot)try{return Array.from(t.shadowRoot.children)}catch(n){return[]}return Array.from(t.children)}function ee(e,t){let n=O(e);for(;n;){if(h(n,t))return n;n=O(n)}let i=e.children||[];for(let s of i)if(h(s,t))return s;let r=L(e);for(let s of r)if(s!==e&&h(s,t))return s;for(let s of r){if(s===e)continue;let a=g(s);for(let o=0;o<a.length;o++)if(h(a[o],t))return a[o]}let l=e.parentElement;for(;l;){let s=L(l);for(let a of s)if(a!==l){if(h(a,t))return a;let o=g(a);for(let f=0;f<o.length;f++)if(h(o[f],t))return o[f]}l=l.parentElement}return null}function te(e,t,n=!1,i=null){let r=e!=null&&e!=="",l=t!=null&&t!=="";if(r&&!l)return H(e,i);if(!r&&l)return j(t,n,i);if(r){if(typeof e!="string")throw new TypeError(`elementType must be a string, got ${typeof e}`);if(!y[e])return console.warn(`Unknown element type: ${e}. Valid types: ${Object.keys(y).join(", ")}`),{elements:[]}}if(l&&typeof t!="string")throw new TypeError(`attributeText must be a string, got ${typeof t}`);let s=[],a=A(window);for(let c of a){let u=g(i||c.document);for(let m=0;m<u.length;m++){let d=u[m];r&&!h(d,e)||l&&!E(d,t,n)||s.push({element:d,frame:c})}}if(s.length===0&&r&&l){let c=[];for(let m of a){let d=g(i||m.document);for(let M=0;M<d.length;M++){let k=d[M];E(k,t,n)&&P(k,t,n)&&c.push({element:k,frame:m})}}let u=new Set;for(let m of c){let d=ee(m.element,e);d&&!u.has(d)&&(u.add(d),s.push({element:d,frame:m.frame}))}}return{elements:(l?s.filter(c=>{let u=c.element;if(P(u,t,n))return!0;for(let d of s)if(d.element!==u&&u.contains(d.element))return!1;return!0}):s).map(c=>{let u=S(c.element),m=c.element.tagName.toLowerCase(),d=N(c.element);return c.frame.isMainFrame?{element:c.element,boundingBox:u,tagName:m,frameIndex:c.frame.frameIndex,isHidden:d}:{boundingBox:u,tagName:m,frameIndex:c.frame.frameIndex,isHidden:d}})}}function _(e){return e?e&&e.elements&&Array.isArray(e.elements)?e.elements:Array.isArray(e)?e:[e]:[]}function ne(e,t="red",n=3){let i=_(e);for(let r=0;r<i.length;r++){let l=i[r],s=l.element?l.element:l;s&&s.style&&(s.style.outline=`${n}px solid ${t}`,s.style.outlineOffset="2px",s.style.boxShadow="0 0 0 2px rgba(255, 255, 255, 0.8)",s.classList.add("elementfinder-highlighted"))}}function re(e){let t=_(e);for(let n=0;n<t.length;n++){let i=t[n],r=i.element?i.element:i;r&&r.style&&(r.style.outline="",r.style.outlineOffset="",r.style.boxShadow="",r.classList.remove("elementfinder-highlighted"))}}var w=[];function oe(){let e=new Map,t=g();for(let r of t)r&&r.style&&r.style.animationPlayState!=="paused"&&(e.set(r,{animationPlayState:r.style.animationPlayState,transitionProperty:r.style.transitionProperty,webkitAnimationPlayState:r.style.webkitAnimationPlayState,webkitTransitionProperty:r.style.webkitTransitionProperty}),r.style.animationPlayState="paused",r.style.transitionProperty="none",r.style.webkitAnimationPlayState="paused",r.style.webkitTransitionProperty="none");let n=document.getElementById("elementfinder-animation-pause");n||(n=document.createElement("style"),n.id="elementfinder-animation-pause",n.textContent=`
1
+ var ElementFinder=(()=>{var P=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Z=(e,t)=>{for(var n in t)P(e,n,{get:t[n],enumerable:!0})},Q=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Y(t))!X.call(e,r)&&r!==n&&P(e,r,{get:()=>t[r],enumerable:!(o=W(t,r))||o.enumerable});return e};var G=e=>Q(P({},"__esModule",{value:!0}),e);var we={};Z(we,{ELEMENT_DEFINITIONS:()=>p,findElements:()=>ce,findElementsByAttribute:()=>V,findElementsByType:()=>F,findProbableElements:()=>me,getAllElements:()=>y,getAllFrames:()=>x,getBoundingBox:()=>A,getElementCounts:()=>fe,getElementDescriptor:()=>le,getSearchableAttributeValues:()=>j,getSearchableAttributes:()=>te,getValidAttributes:()=>be,getValidTypes:()=>ye,highlight:()=>de,isHidden:()=>k,matchesAttribute:()=>S,matchesType:()=>g,parseCondition:()=>H,parseXPath:()=>w,pauseAnimations:()=>ge,resumeAnimations:()=>pe,setSearchableAttributes:()=>ee,splitByOperator:()=>B,unhighlight:()=>he});var D={link:"self::a or @role='link' or @href",navigation:"@role='navigation' or self::nav",heading:"@role='heading' or self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6",button:"self::button or @role='button' or @type='button' or @type='submit'",checkbox:"(self::input and @type='checkbox') or @role='checkbox'",switch:"(self::input and @type='checkbox') or @role='switch' or (self::button and (contains(@class, 'switch') or @data-state))",slider:"self::input[@type='range'] or @role='slider'",datepicker:"self::input[@type='date'] or @role='date'",colorpicker:"self::input[@type='color'] or @role='color'",radio:"(self::input and @type='radio') or @role='radio'",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'])",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'",file:"self::input and @type='file'",list:"self::ul or self::ol or @role='list'",listitem:"self::li or @role='listitem'",menu:"self::menu or @role='menu'",menuitem:"@role='menuitem'",toolbar:"@role='toolbar'",dialog:"@role='dialog' or @role='alertdialog'",table:"self::table or @role='table'",row:"self::tr or @role='row'",column:"self::td or self::th or @role='cell' or @role='gridcell' or @role='columnheader'",cell:"self::td or @role='cell' or @role='gridcell'",image:"self::img or @role='img' or @alt",element:"true()"};var R=["placeholder","value","data-value","data-test-id","data-testid","id","resource-id","name","aria-label","hint","title","tooltip","alt","src","aria-labelledby"];var b={selfWithTag:/^self::([a-zA-Z0-9-]+)(?:\[([^\]]+)\])?$/,contains:/contains\(@([a-zA-Z0-9-]+),\s*['"]([^'"]+)['"]\)/i,attrEquals:/@([a-zA-Z0-9-]+)\s*=\s*['"]([^'"]*)['"]/,attrExists:/^@([a-zA-Z0-9-]+)$/,descendant:/descendant::([a-zA-Z0-9-]+)/i,ancestor:/ancestor::\*\[([^\]]+)\]/i,operatorOr:/^\s*\bor\b\s*/i,operatorAnd:/^\s*\band\b\s*/i},v=100,C=new Map;for(let[e,t]of Object.entries(D))t==="true()"?C.set(e,()=>!0):C.set(e,n=>w(t,n));var E=R;function ee(e){if(!Array.isArray(e))throw new TypeError("attributes must be an array");E=e}function te(){return[...E]}function j(e){if(e==null||e.nodeType!==Node.ELEMENT_NODE)return{};let t={},n=E;for(let o=0;o<n.length;o++){let r=n[o],i;try{i=e.getAttribute(r)}catch(l){continue}i!=null&&i!==""&&(t[r]=i)}return t}function T(e){return e==null?"":String(e).replace(/\s+/g," ").trim()}function ne(e){let t=T(e);if(!t)return"";let n=t.split(/[?#]/)[0],o=Math.max(n.lastIndexOf("/"),n.lastIndexOf("\\")),r=o>=0?n.slice(o+1):n,i=r.lastIndexOf(".");return i>0?r.slice(0,i):r}function re(e){let t=e.getAttribute("aria-labelledby");if(!t)return"";let n=t.split(/\s+/),o=e.ownerDocument||document,r="";for(let i of n)try{let l=o.getElementById(i);if(!l)continue;let s=T(l.textContent);s&&(r=r?`${r} ${s}`:s)}catch(l){}return r}function q(e){let t=j(e),n=E;for(let i=0;i<n.length;i++){let l=n[i];if(!Object.prototype.hasOwnProperty.call(t,l))continue;let s=l==="aria-labelledby"?re(e):l==="src"?ne(t[l]):t[l],a=T(s);if(a)return{attributeName:l,identifiableText:a}}let o=T(L(e));if(o)return{attributeName:"text",identifiableText:o};let r=T(e.textContent);return r?{attributeName:"text",identifiableText:r}:null}function oe(e){if(!e||!e.ownerDocument)return null;try{let t=x(window);for(let n=0;n<t.length;n++)if(t[n].document===e.ownerDocument)return t[n].document}catch(t){}return e.ownerDocument}function ie(e,t){let n=oe(e);if(!n)return{index:1};let o=y(n),r=1,i=0;for(let l=0;l<o.length;l++){let s=o[l];if(s!==e&&(s===n.documentElement||s.tagName==="BODY"))continue;let a=q(s);!a||a.identifiableText!==t||(i++,s===e&&(r=i))}return{index:r}}function se(e){if(e==null||e.nodeType!==Node.ELEMENT_NODE)return null;let t=Object.keys(p);for(let n=0;n<t.length;n++){let o=t[n];if(o!=="element"&&g(e,o))return o}return"element"}function le(e){if(e==null||e.nodeType!==Node.ELEMENT_NODE)return{identifiableText:null,attributeName:null,index:1,type:null,tagName:null};let t=se(e),n=q(e);if(!n)return{identifiableText:null,attributeName:null,index:1,type:t,tagName:e.tagName.toLowerCase()};let o=ie(e,n.identifiableText);return{identifiableText:n.identifiableText,attributeName:n.attributeName,index:o.index,type:t,tagName:e.tagName.toLowerCase()}}function w(e,t,n=0){if(e==null||t==null)return!1;if(n>v)throw new Error("XPath expression exceeds maximum recursion depth");if(e=e.trim(),e==="true()")return!0;if(e[0]==="("&&e[e.length-1]===")"){let i=1,l=!0;for(let s=1;s<e.length-1;s++)if(e[s]==="("?i++:e[s]===")"&&i--,i===0){l=!1;break}if(l)return w(e.slice(1,-1),t,n+1)}let o=B(e,"or");if(o.length>1){for(let i of o)if(w(i,t,n+1))return!0;return!1}let r=B(e,"and");if(r.length>1){for(let i of r)if(!w(i,t,n+1))return!1;return!0}return H(e,t,n)}function B(e,t){let n=[],o=0,r="",i=!1,l="",s=t==="or"?b.operatorOr:b.operatorAnd;for(let a=0;a<e.length;a++){let f=e[a];if((f==="'"||f==='"')&&(a===0||e[a-1]!=="\\")&&(i?f===l&&(i=!1):(i=!0,l=f)),!i&&(f==="("?o++:f===")"&&o--,o===0)){let c=e.slice(a).match(s);if(c){n.push(r.trim()),a+=c[0].length-1,r="";continue}}r+=f}return r.trim()&&n.push(r.trim()),n}function H(e,t,n=0){if(e==null||t==null)return!1;e=e.trim();let o=e.match(b.selfWithTag);if(o){let f=o[1].toUpperCase();return t.tagName!==f?!1:o[2]?w(o[2],t,n+1):!0}let r=e.match(b.contains);if(r)return(t.getAttribute(r[1])||"").toLowerCase().includes(r[2].toLowerCase());let i=e.match(b.attrEquals);if(i)return t.getAttribute(i[1])===i[2];let l=e.match(b.attrExists);if(l)return t.hasAttribute(l[1]);let s=e.match(b.descendant);if(s)return t.querySelector(s[1])!==null;let a=e.match(b.ancestor);if(a){let f=t.parentElement;for(;f;){if(w(a[1],f,n+1))return!0;f=f.parentElement}return!1}return!1}var p=Object.freeze(D);function L(e){let t="";for(let n=0;n<e.childNodes.length;n++){let o=e.childNodes[n];o.nodeType===Node.TEXT_NODE&&(t+=o.textContent)}return t.trim()}function ae(e){if(e.tagName==="STYLE"||e.tagName==="SCRIPT"||e.querySelector("STYLE, SCRIPT"))return!0;let t=e.parentElement;for(;t;){if(t.tagName==="STYLE"||t.tagName==="SCRIPT")return!0;t=t.parentElement}return!1}function _(e){let t=e.getAttribute("aria-labelledby");if(!t)return"";let n=t.split(/\s+/),o="";for(let r of n)try{let i=document.getElementById(r);i&&(o+=i.textContent)}catch(i){}return o}function S(e,t,n=!1){if(e==null)return!1;if(t==null||t==="")return!0;if(ae(e))return!1;let o=E;for(let l=0;l<o.length;l++){let s=o[l],a;try{a=e.getAttribute(s)}catch(f){continue}if(a){if(s==="aria-labelledby"){if(n?a===t:a.includes(t))return!0;let f=_(e);if(f&&(n?f===t:f.includes(t)))return!0}else if(n?a===t:a.includes(t))return!0}}let r=L(e);if(n?r===t:r.includes(t))return!0;let i=e.textContent;return!!(n?i.trim()===t:i.includes(t))}function g(e,t){if(e==null)return!1;let n=C.get(t);return n?n(e):!1}function y(e=document){let t=[];if(e==null)return t;let n=e.nodeType===Node.DOCUMENT_NODE?e.documentElement:e;if(!n)return t;let o=[n];for(;o.length>0;){let r=o.pop();if(r.nodeType!==Node.ELEMENT_NODE||r.tagName==="SCRIPT"||r.tagName==="STYLE")continue;t.push(r);let i=r.children;for(let l=i.length-1;l>=0;l--)o.push(i[l]);try{if(r.shadowRoot){let l=r.shadowRoot.children;for(let s=l.length-1;s>=0;s--)o.push(l[s])}}catch(l){}}return t}function x(e=window){let t=[];try{t.push({window:e,document:e.document,isMainFrame:!0,frameIndex:-1});let n=e.document.querySelectorAll("iframe");for(let o=0;o<n.length;o++){let r=n[o];try{r.contentWindow&&r.contentDocument&&t.push({window:r.contentWindow,document:r.contentDocument,isMainFrame:!1,frameElement:r,frameIndex:o})}catch(i){i.name==="SecurityError"?console.warn("Skipping cross-origin iframe:",i.message):console.warn("Error accessing iframe:",i.message)}}}catch(n){console.warn("Error getting frames:",n.message)}return t}function A(e){let t=e.getBoundingClientRect();return{x:t.x,y:t.y,width:t.width,height:t.height,top:t.top,bottom:t.bottom,left:t.left,right:t.right,midx:t.x+t.width/2,midy:t.y+t.height/2,tagName:e.tagName.toLowerCase()}}function k(e){if(e==null||e.offsetWidth===0&&e.offsetHeight===0)return!0;try{let t=window.getComputedStyle(e);if(t.visibility==="hidden"||t.visibility==="collapse"||t.display==="none")return!0}catch(t){}return!!e.hasAttribute("hidden")}function F(e="element",t=null,n=null){if(e==null&&(e="element"),typeof e!="string")throw new TypeError(`type must be a string, got ${typeof e}`);let o=n&&n.failOnUnknownType===!0;if(e&&!p[e]){let a=`Unknown element type: ${e}. Valid types: ${Object.keys(p).join(", ")}`;if(o)throw new TypeError(`Unknown element type: ${e}`);return console.warn(a),{elements:[]}}let r=[],i=x(window);for(let a of i){let f=y(t||a.document);for(let u=0;u<f.length;u++){let c=f[u];e&&!g(c,e)||r.push({element:c,frame:a})}}let l=[];if(r.length>0){let a=new Set(r.map(u=>u.element)),f=new Set;for(let u=r.length-1;u>=0;u--){let c=r[u],m=c.element;if(!f.has(m)){l.unshift(c);let d=m.parentElement;for(;d;)a.has(d)&&f.add(d),d=d.parentElement}}}return{elements:l.map(a=>{let f=A(a.element),u=a.element.tagName.toLowerCase(),c=k(a.element);return a.frame.isMainFrame?{element:a.element,boundingBox:f,tagName:u,frameIndex:a.frame.frameIndex,isHidden:c}:{boundingBox:f,tagName:u,frameIndex:a.frame.frameIndex,isHidden:c}})}}function V(e,t=!1,n=null){if(e==null&&(e=""),typeof e!="string")throw new TypeError(`value must be a string, got ${typeof e}`);let o=[],r=x(window);for(let s of r){let a=y(n||s.document);for(let f=0;f<a.length;f++){let u=a[f];S(u,e,t)&&o.push({element:u,frame:s})}}return{elements:o.filter(s=>{let a=s.element;if(I(a,e,t))return!0;for(let u of o)if(u.element!==a&&a.contains(u.element))return!1;return!0}).map(s=>{let a=A(s.element),f=s.element.tagName.toLowerCase(),u=k(s.element);return s.frame.isMainFrame?{element:s.element,boundingBox:a,tagName:f,frameIndex:s.frame.frameIndex,isHidden:u}:{boundingBox:a,tagName:f,frameIndex:s.frame.frameIndex,isHidden:u}})}}function I(e,t,n=!1){if(t==null||t==="")return!0;let o=E;for(let i=0;i<o.length;i++){let l=o[i],s;try{s=e.getAttribute(l)}catch(a){continue}if(s){if(l==="aria-labelledby"){if(n?s===t:s.includes(t))return!0;let a=_(e);if(a&&(n?a===t:a.includes(t)))return!0}else if(n?s===t:s.includes(t))return!0}}let r=L(e);return!!(n?r===t:r.includes(t))}function fe(e=null,t=null,n=null){let o=e!=null;if(o){if(typeof e!="string")throw new TypeError(`type must be a string, got ${typeof e}`);if(!p[e]){let s=`Unknown element type: ${e}. Valid types: ${Object.keys(p).join(", ")}`;if(n&&n.failOnUnknownType===!0)throw new TypeError(`Unknown element type: ${e}`);return console.warn(s),{[e]:0}}}let r={},i=o?[e]:Object.keys(p).filter(s=>s!=="element");for(let s=0;s<i.length;s++)r[i[s]]=0;let l=x(window);for(let s=0;s<l.length;s++){let a=l[s],f=y(t||a.document);for(let u=0;u<f.length;u++){let c=f[u];for(let m=0;m<i.length;m++){let d=i[m];g(c,d)&&(r[d]+=1)}}}return r}function ce(e=null,t=null,n=!1,o=null,r=null){if(t==null&&(t=""),e!=null){if(typeof e!="string")throw new TypeError(`type must be a string, got ${typeof e}`);if(!p[e]){let f=`Unknown element type: ${e}. Valid types: ${Object.keys(p).join(", ")}`;if(r&&r.failOnUnknownType===!0)throw new TypeError(`Unknown element type: ${e}`);return console.warn(f),{elements:[]}}}if(t!==""&&typeof t!="string")throw new TypeError(`text must be a string, got ${typeof t}`);let i=[],l=x(window);for(let f of l){let u=y(o||f.document);for(let c=0;c<u.length;c++){let m=u[c];e!=null&&!g(m,e)||t!==""&&!S(m,t,n)||i.push({element:m,frame:f})}}return{elements:(t!==""?i.filter(f=>{let u=f.element;if(I(u,t,n))return!0;for(let m of i)if(m.element!==u&&u.contains(m.element))return!1;return!0}):i).map(f=>{let u=A(f.element),c=f.element.tagName.toLowerCase(),m=k(f.element);return f.frame.isMainFrame?{element:f.element,boundingBox:u,tagName:c,frameIndex:f.frame.frameIndex,isHidden:m}:{boundingBox:u,tagName:c,frameIndex:f.frame.frameIndex,isHidden:m}})}}function $(e){if(e.parentElement)return e.parentElement;try{let t=e.getRootNode();if(t&&t.host)return t.host}catch(t){}return null}function U(e){let t=$(e);if(!t)return[];if(t.shadowRoot)try{return Array.from(t.shadowRoot.children)}catch(n){return[]}return Array.from(t.children)}function ue(e,t){let n=$(e);for(;n;){if(g(n,t))return n;n=$(n)}let o=e.children||[];for(let l of o)if(g(l,t))return l;let r=U(e);for(let l of r)if(l!==e&&g(l,t))return l;for(let l of r){if(l===e)continue;let s=y(l);for(let a=0;a<s.length;a++)if(g(s[a],t))return s[a]}let i=e.parentElement;for(;i;){let l=U(i);for(let s of l)if(s!==i){if(g(s,t))return s;let a=y(s);for(let f=0;f<a.length;f++)if(g(a[f],t))return a[f]}i=i.parentElement}return null}function me(e,t,n=!1,o=null,r=null){let i=e!=null&&e!=="",l=t!=null&&t!=="";if(i&&!l)return F(e,o,r);if(!i&&l)return V(t,n,o);if(i){if(typeof e!="string")throw new TypeError(`elementType must be a string, got ${typeof e}`);if(!p[e]){let c=`Unknown element type: ${e}. Valid types: ${Object.keys(p).join(", ")}`;if(r&&r.failOnUnknownType===!0)throw new TypeError(`Unknown element type: ${e}`);return console.warn(c),{elements:[]}}}if(l&&typeof t!="string")throw new TypeError(`attributeText must be a string, got ${typeof t}`);let s=[],a=x(window);for(let c of a){let m=y(o||c.document);for(let d=0;d<m.length;d++){let h=m[d];i&&!g(h,e)||l&&!S(h,t,n)||s.push({element:h,frame:c})}}if(s.length===0&&i&&l){let c=[];for(let d of a){let h=y(o||d.document);for(let M=0;M<h.length;M++){let O=h[M];S(O,t,n)&&I(O,t,n)&&c.push({element:O,frame:d})}}let m=new Set;for(let d of c){let h=ue(d.element,e);h&&!m.has(h)&&(m.add(h),s.push({element:h,frame:d.frame}))}}return{elements:(l?s.filter(c=>{let m=c.element;if(I(m,t,n))return!0;for(let h of s)if(h.element!==m&&m.contains(h.element))return!1;return!0}):s).map(c=>{let m=A(c.element),d=c.element.tagName.toLowerCase(),h=k(c.element);return c.frame.isMainFrame?{element:c.element,boundingBox:m,tagName:d,frameIndex:c.frame.frameIndex,isHidden:h}:{boundingBox:m,tagName:d,frameIndex:c.frame.frameIndex,isHidden:h}})}}function z(e){return e?e&&e.elements&&Array.isArray(e.elements)?e.elements:Array.isArray(e)?e:[e]:[]}function de(e,t="red",n=3){let o=z(e);for(let r=0;r<o.length;r++){let i=o[r],l=i.element?i.element:i;l&&l.style&&(l.style.outline=`${n}px solid ${t}`,l.style.outlineOffset="2px",l.style.boxShadow="0 0 0 2px rgba(255, 255, 255, 0.8)",l.classList.add("elementfinder-highlighted"))}}function he(e){let t=z(e);for(let n=0;n<t.length;n++){let o=t[n],r=o.element?o.element:o;r&&r.style&&(r.style.outline="",r.style.outlineOffset="",r.style.boxShadow="",r.classList.remove("elementfinder-highlighted"))}}var N=[];function ge(){let e=new Map,t=y();for(let r of t)r&&r.style&&r.style.animationPlayState!=="paused"&&(e.set(r,{animationPlayState:r.style.animationPlayState,transitionProperty:r.style.transitionProperty,webkitAnimationPlayState:r.style.webkitAnimationPlayState,webkitTransitionProperty:r.style.webkitTransitionProperty}),r.style.animationPlayState="paused",r.style.transitionProperty="none",r.style.webkitAnimationPlayState="paused",r.style.webkitTransitionProperty="none");let n=document.getElementById("elementfinder-animation-pause");n||(n=document.createElement("style"),n.id="elementfinder-animation-pause",n.textContent=`
2
2
  *, *::before, *::after {
3
3
  animation-play-state: paused !important;
4
4
  transition-property: none !important;
@@ -12,4 +12,4 @@ var ElementFinder=(()=>{var I=Object.defineProperty;var F=Object.getOwnPropertyD
12
12
  transition-duration: 0s !important;
13
13
  }
14
14
  }
15
- `,document.head.appendChild(n));let i={originalStyles:e,pausedCount:e.size};return w.push(i),i}function ie(e){if(e){let n=w.indexOf(e);if(n===-1)return;w.splice(n,1)}else{if(w.length===0)return;e=w.pop()}let t=e.originalStyles;if(t)for(let[n,i]of t)n&&n.style&&(n.style.animationPlayState=i.animationPlayState||"",n.style.transitionProperty=i.transitionProperty||"",n.style.webkitAnimationPlayState=i.webkitAnimationPlayState||"",n.style.webkitTransitionProperty=i.webkitTransitionProperty||"");if(w.length===0){let n=document.getElementById("elementfinder-animation-pause");n&&n.remove()}}function se(){return Object.keys(y)}function ae(){return[...x]}return X(le);})();
15
+ `,document.head.appendChild(n));let o={originalStyles:e,pausedCount:e.size};return N.push(o),o}function pe(e){if(e){let n=N.indexOf(e);if(n===-1)return;N.splice(n,1)}else{if(N.length===0)return;e=N.pop()}let t=e.originalStyles;if(t)for(let[n,o]of t)n&&n.style&&(n.style.animationPlayState=o.animationPlayState||"",n.style.transitionProperty=o.transitionProperty||"",n.style.webkitAnimationPlayState=o.webkitAnimationPlayState||"",n.style.webkitTransitionProperty=o.webkitTransitionProperty||"");if(N.length===0){let n=document.getElementById("elementfinder-animation-pause");n&&n.remove()}}function ye(){return Object.keys(p)}function be(){return[...E]}return G(we);})();