@reticlehq/browser 3.7.0 → 3.8.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.
Files changed (92) hide show
  1. package/dist/actions/action-report.d.ts +10 -0
  2. package/dist/actions/action-report.js +29 -2
  3. package/dist/actions/actions-dom.d.ts +4 -0
  4. package/dist/actions/actions-dom.js +20 -4
  5. package/dist/actions/actions.d.ts +1 -0
  6. package/dist/actions/actions.js +35 -17
  7. package/dist/actions/appeared-text.js +2 -2
  8. package/dist/actions/click-target.d.ts +1 -0
  9. package/dist/actions/click-target.js +14 -0
  10. package/dist/actions/danger-context.d.ts +2 -1
  11. package/dist/actions/danger-context.js +20 -5
  12. package/dist/activate.d.ts +14 -0
  13. package/dist/activate.js +43 -0
  14. package/dist/busy.d.ts +3 -0
  15. package/dist/busy.js +42 -0
  16. package/dist/commands/commands.js +45 -17
  17. package/dist/connect-options.d.ts +12 -1
  18. package/dist/connect-options.js +18 -1
  19. package/dist/dom/a11y.d.ts +2 -0
  20. package/dist/dom/a11y.js +191 -159
  21. package/dist/dom/chart.d.ts +3 -2
  22. package/dist/dom/chart.js +3 -2
  23. package/dist/dom/dom-ignore.d.ts +6 -0
  24. package/dist/dom/dom-ignore.js +43 -1
  25. package/dist/dom/layout-audit.d.ts +15 -0
  26. package/dist/dom/layout-audit.js +208 -0
  27. package/dist/dom/paint-context.d.ts +15 -0
  28. package/dist/dom/paint-context.js +78 -0
  29. package/dist/dom/query.d.ts +2 -1
  30. package/dist/dom/query.js +97 -34
  31. package/dist/dom/shadow-registry.d.ts +1 -0
  32. package/dist/dom/shadow-registry.js +27 -0
  33. package/dist/dom/snapshot.d.ts +5 -0
  34. package/dist/dom/snapshot.js +85 -34
  35. package/dist/dom/stable-selector.d.ts +1 -0
  36. package/dist/dom/stable-selector.js +19 -0
  37. package/dist/dom/text-match.d.ts +12 -0
  38. package/dist/dom/text-match.js +115 -0
  39. package/dist/dom/theme.d.ts +1 -0
  40. package/dist/dom/theme.js +21 -9
  41. package/dist/dom/virtualized.js +5 -1
  42. package/dist/index.d.ts +2 -1
  43. package/dist/index.js +5 -0
  44. package/dist/observers/blind-spots.js +3 -2
  45. package/dist/observers/canvas.d.ts +18 -0
  46. package/dist/observers/canvas.js +76 -0
  47. package/dist/observers/dom.js +66 -18
  48. package/dist/observers/field.d.ts +11 -0
  49. package/dist/observers/field.js +117 -13
  50. package/dist/observers/install-all.js +4 -0
  51. package/dist/observers/media.d.ts +2 -0
  52. package/dist/observers/media.js +32 -0
  53. package/dist/observers/net-detail/network-body.d.ts +2 -0
  54. package/dist/observers/net-detail/network-body.js +8 -1
  55. package/dist/observers/network.js +26 -6
  56. package/dist/observers/route.d.ts +2 -0
  57. package/dist/observers/route.js +33 -4
  58. package/dist/presenter/chrome/presenter-report-copy.d.ts +0 -5
  59. package/dist/presenter/chrome/presenter-report-copy.js +0 -14
  60. package/dist/presenter/hud-telemetry.js +1 -1
  61. package/dist/presenter/presenter-chat-views.d.ts +3 -4
  62. package/dist/presenter/presenter-chat-views.js +47 -20
  63. package/dist/presenter/presenter-config.d.ts +1 -0
  64. package/dist/presenter/presenter-config.js +1 -0
  65. package/dist/presenter/presenter-frame.d.ts +3 -2
  66. package/dist/presenter/presenter-frame.js +24 -15
  67. package/dist/presenter/presenter-harness-row.d.ts +4 -15
  68. package/dist/presenter/presenter-harness-row.js +11 -50
  69. package/dist/presenter/presenter-report-styles.js +0 -17
  70. package/dist/presenter/presenter-report.d.ts +1 -2
  71. package/dist/presenter/presenter-report.js +4 -54
  72. package/dist/presenter/presenter-run-state.js +2 -1
  73. package/dist/presenter/presenter-settings.d.ts +2 -3
  74. package/dist/presenter/presenter-settings.js +14 -17
  75. package/dist/presenter/presenter-shell.d.ts +6 -4
  76. package/dist/presenter/presenter-shell.js +37 -7
  77. package/dist/presenter/presenter.js +32 -11
  78. package/dist/registry/store-adapters.d.ts +5 -2
  79. package/dist/registry/store-adapters.js +40 -10
  80. package/dist/reticle-inject.js +602 -611
  81. package/dist/reticle.d.ts +4 -2
  82. package/dist/reticle.js +36 -6
  83. package/dist/security/page-location.d.ts +2 -0
  84. package/dist/security/page-location.js +7 -0
  85. package/dist/security/serialization.js +1 -1
  86. package/dist/storage-keys.d.ts +1 -0
  87. package/dist/storage-keys.js +1 -0
  88. package/dist/timers/native/native-timers.d.ts +1 -0
  89. package/dist/timers/native/native-timers.js +5 -0
  90. package/dist/transport/transport.d.ts +1 -0
  91. package/dist/transport/transport.js +18 -0
  92. package/package.json +6 -2
package/dist/dom/a11y.js CHANGED
@@ -2,7 +2,8 @@ import { readTestId } from './addressing/testid-attr.js';
2
2
  import { DEFAULT_TESTID_ATTR, ElementState, REDACTED_VALUE, } from '@reticlehq/core';
3
3
  import { isButton, isHtmlElement, isImage, isInput, isMeter, isOutput, isProgress, isSelect, isTextArea, } from './realm.js';
4
4
  import { refs } from './addressing/refs.js';
5
- import { capturedRootOf } from './shadow-registry.js';
5
+ import { parentAcrossShadowBoundary } from './shadow-registry.js';
6
+ import { clippingBox, overflowAxes, scrolledInto, } from './paint-context.js';
6
7
  import { inspectChart } from './chart.js';
7
8
  import { isSensitiveKey } from '../security/serialization.js';
8
9
  import { formatSource, sourceFromDom } from './addressing/source.js';
@@ -35,36 +36,46 @@ const NAME_FROM_CONTENT = new Set([
35
36
  const INPUT_TEXT_TYPES = new Set(['text', 'email', 'tel', 'url', 'password', '']);
36
37
  function inputRole(input) {
37
38
  const type = input.type.toLowerCase();
38
- if ('search' === type)
39
+ if ('search' === type) {
39
40
  return 'searchbox';
40
- if (INPUT_TEXT_TYPES.has(type))
41
+ }
42
+ if (INPUT_TEXT_TYPES.has(type)) {
41
43
  return 'textbox';
42
- if ('checkbox' === type)
44
+ }
45
+ if ('checkbox' === type) {
43
46
  return 'checkbox';
44
- if ('radio' === type)
47
+ }
48
+ if ('radio' === type) {
45
49
  return 'radio';
46
- if ('range' === type)
50
+ }
51
+ if ('range' === type) {
47
52
  return 'slider';
48
- if ('number' === type)
53
+ }
54
+ if ('number' === type) {
49
55
  return 'spinbutton';
50
- if ('submit' === type || 'button' === type || 'reset' === type)
56
+ }
57
+ if ('submit' === type || 'button' === type || 'reset' === type) {
51
58
  return 'button';
59
+ }
52
60
  return 'textbox';
53
61
  }
54
62
  function hasAuthorNaming(el) {
55
63
  const label = el.getAttribute('aria-label');
56
- if (label !== null && label.trim().length > 0)
64
+ if (label !== null && label.trim().length > 0) {
57
65
  return true;
66
+ }
58
67
  const labelledby = el.getAttribute('aria-labelledby');
59
- if (labelledby !== null && labelledby.trim().length > 0)
68
+ if (labelledby !== null && labelledby.trim().length > 0) {
60
69
  return true;
70
+ }
61
71
  const title = el.getAttribute('title');
62
72
  return title !== null && title.trim().length > 0;
63
73
  }
64
74
  export function getRole(el) {
65
75
  const explicit = el.getAttribute('role');
66
- if (explicit !== null && explicit.trim().length > 0)
76
+ if (explicit !== null && explicit.trim().length > 0) {
67
77
  return explicit.trim();
78
+ }
68
79
  const tag = el.tagName.toLowerCase();
69
80
  switch (tag) {
70
81
  case 'a':
@@ -154,25 +165,30 @@ function collapse(text) {
154
165
  }
155
166
  function labelledByText(el) {
156
167
  const ids = el.getAttribute('aria-labelledby');
157
- if (null === ids)
168
+ if (null === ids) {
158
169
  return null;
170
+ }
159
171
  const parts = [];
160
172
  for (const id of ids.split(/\s+/)) {
161
173
  const ref = el.ownerDocument.getElementById(id);
162
- if (ref !== null)
174
+ if (ref !== null) {
163
175
  parts.push(collapse(textWithoutHidden(ref)));
176
+ }
164
177
  }
165
178
  const joined = parts.join(' ').trim();
166
179
  return joined.length > 0 ? joined : null;
167
180
  }
168
181
  function textWithoutHidden(node) {
169
- if (node.nodeType === Node.TEXT_NODE)
182
+ if (node.nodeType === Node.TEXT_NODE) {
170
183
  return node.textContent ?? '';
171
- if (node.nodeType !== Node.ELEMENT_NODE)
184
+ }
185
+ if (node.nodeType !== Node.ELEMENT_NODE) {
172
186
  return '';
187
+ }
173
188
  const el = node;
174
- if ('true' === el.getAttribute('aria-hidden'))
189
+ if ('true' === el.getAttribute('aria-hidden')) {
175
190
  return '';
191
+ }
176
192
  if (isImage(el)) {
177
193
  const alt = el.getAttribute('alt');
178
194
  return null === alt ? '' : alt;
@@ -181,11 +197,13 @@ function textWithoutHidden(node) {
181
197
  let lastWasElement = false;
182
198
  for (const child of el.childNodes) {
183
199
  const piece = textWithoutHidden(child);
184
- if (0 === piece.length)
200
+ if (0 === piece.length) {
185
201
  continue;
202
+ }
186
203
  const isEl = Node.ELEMENT_NODE === child.nodeType;
187
- if (result.length > 0 && (isEl || lastWasElement))
204
+ if (result.length > 0 && (isEl || lastWasElement)) {
188
205
  result += ' ';
206
+ }
189
207
  result += piece;
190
208
  lastWasElement = isEl;
191
209
  }
@@ -193,15 +211,18 @@ function textWithoutHidden(node) {
193
211
  }
194
212
  export function getAccessibleName(el) {
195
213
  const labelled = labelledByText(el);
196
- if (labelled !== null)
214
+ if (labelled !== null) {
197
215
  return labelled;
216
+ }
198
217
  const ariaLabel = el.getAttribute('aria-label');
199
- if (ariaLabel !== null && ariaLabel.trim().length > 0)
218
+ if (ariaLabel !== null && ariaLabel.trim().length > 0) {
200
219
  return ariaLabel.trim();
220
+ }
201
221
  if (isImage(el)) {
202
222
  const alt = el.getAttribute('alt');
203
- if (alt !== null)
223
+ if (alt !== null) {
204
224
  return alt.trim();
225
+ }
205
226
  }
206
227
  const isLabelable = isInput(el) ||
207
228
  isTextArea(el) ||
@@ -217,16 +238,36 @@ export function getAccessibleName(el) {
217
238
  .map((l) => collapse(textWithoutHidden(l)))
218
239
  .join(' ')
219
240
  .trim();
220
- if (text.length > 0)
241
+ if (text.length > 0) {
221
242
  return text;
243
+ }
222
244
  }
223
245
  }
224
246
  if ('fieldset' === el.tagName.toLowerCase()) {
225
247
  const legend = [...el.children].find((child) => 'legend' === child.tagName.toLowerCase());
226
248
  if (legend !== undefined) {
227
249
  const text = collapse(textWithoutHidden(legend));
228
- if (text.length > 0)
250
+ if (text.length > 0) {
251
+ return text;
252
+ }
253
+ }
254
+ }
255
+ if ('table' === el.tagName.toLowerCase()) {
256
+ const caption = [...el.children].find((child) => 'caption' === child.tagName.toLowerCase());
257
+ if (caption !== undefined) {
258
+ const text = collapse(textWithoutHidden(caption));
259
+ if (text.length > 0) {
260
+ return text;
261
+ }
262
+ }
263
+ }
264
+ if ('figure' === el.tagName.toLowerCase()) {
265
+ const figcaption = [...el.children].find((child) => 'figcaption' === child.tagName.toLowerCase());
266
+ if (figcaption !== undefined) {
267
+ const text = collapse(textWithoutHidden(figcaption));
268
+ if (text.length > 0) {
229
269
  return text;
270
+ }
230
271
  }
231
272
  }
232
273
  if (isInput(el) || isTextArea(el) || isSelect(el)) {
@@ -234,49 +275,67 @@ export function getAccessibleName(el) {
234
275
  const type = el.type.toLowerCase();
235
276
  if ('submit' === type || 'button' === type || 'reset' === type) {
236
277
  const value = collapse(el.value);
237
- if (value.length > 0)
278
+ if (value.length > 0) {
238
279
  return value;
280
+ }
239
281
  }
240
282
  }
241
283
  if (isInput(el) || isTextArea(el)) {
242
284
  const placeholder = el.getAttribute('placeholder');
243
- if (placeholder !== null && placeholder.trim().length > 0)
285
+ if (placeholder !== null && placeholder.trim().length > 0) {
244
286
  return placeholder.trim();
287
+ }
245
288
  }
246
289
  }
247
290
  if (NAME_FROM_CONTENT.has(getRole(el))) {
248
291
  const text = collapse(textWithoutHidden(el));
249
- if (text.length > 0)
292
+ if (text.length > 0) {
250
293
  return text;
294
+ }
251
295
  }
252
296
  const title = el.getAttribute('title');
253
- if (title !== null && title.trim().length > 0)
297
+ if (title !== null && title.trim().length > 0) {
254
298
  return title.trim();
299
+ }
255
300
  return '';
256
301
  }
257
302
  function ariaBool(el, attr) {
258
303
  const value = el.getAttribute(attr);
259
- if (null === value)
304
+ if (null === value) {
260
305
  return undefined;
306
+ }
261
307
  return 'true' === value;
262
308
  }
263
309
  export function getStates(el, visible = isVisible(el)) {
264
310
  const states = [ElementState.PRESENT];
265
311
  states.push(visible ? ElementState.VISIBLE : ElementState.HIDDEN);
266
- const disabledProp = (isButton(el) || isInput(el) || isSelect(el) || isTextArea(el)) && el.disabled;
267
- const disabled = disabledProp || true === ariaBool(el, 'aria-disabled');
312
+ const disabled = el.matches(':disabled') || true === ariaBool(el, 'aria-disabled');
268
313
  states.push(disabled ? ElementState.DISABLED : ElementState.ENABLED);
269
314
  const checkedProp = isInput(el) && ('checkbox' === el.type || 'radio' === el.type) && el.checked;
270
- if (checkedProp || true === ariaBool(el, 'aria-checked'))
315
+ if (checkedProp || true === ariaBool(el, 'aria-checked')) {
271
316
  states.push(ElementState.CHECKED);
272
- if (true === ariaBool(el, 'aria-expanded'))
317
+ }
318
+ if (true === ariaBool(el, 'aria-expanded') || isOpenDetailsSummary(el)) {
273
319
  states.push(ElementState.EXPANDED);
274
- if (true === ariaBool(el, 'aria-pressed'))
320
+ }
321
+ if (true === ariaBool(el, 'aria-pressed')) {
275
322
  states.push(ElementState.PRESSED);
276
- if (el.ownerDocument.activeElement === el)
323
+ }
324
+ if (el.ownerDocument.activeElement === el) {
277
325
  states.push(ElementState.FOCUSED);
326
+ }
278
327
  return states;
279
328
  }
329
+ function isOpenDetailsSummary(el) {
330
+ const details = el.parentElement;
331
+ if (HTML_SUMMARY_TAG !== el.localName ||
332
+ null === details ||
333
+ HTML_DETAILS_TAG !== details.localName) {
334
+ return false;
335
+ }
336
+ const summary = Array.from(details.children).find((child) => HTML_SUMMARY_TAG === child.localName);
337
+ return summary === el && details.hasAttribute(HTML_DETAILS_OPEN_ATTRIBUTE);
338
+ }
280
339
  const SENSITIVE_AUTOCOMPLETE = /\b(cc-|current-password|new-password|one-time-code)/i;
281
340
  export function isSensitiveField(el) {
282
341
  if (!isInput(el) && !isTextArea(el) && !isSelect(el)) {
@@ -297,8 +356,9 @@ export function isSensitiveField(el) {
297
356
  }
298
357
  export function getValue(el) {
299
358
  if (isInput(el) || isTextArea(el) || isSelect(el)) {
300
- if (isSensitiveField(el))
359
+ if (isSensitiveField(el)) {
301
360
  return '' === el.value ? '' : REDACTED_VALUE;
361
+ }
302
362
  return el.value;
303
363
  }
304
364
  const valueNow = el.getAttribute('aria-valuenow');
@@ -306,62 +366,79 @@ export function getValue(el) {
306
366
  }
307
367
  function hiddenInsideClosedDetails(el) {
308
368
  const parent = el.parentElement;
309
- if (null === parent)
369
+ if (null === parent) {
310
370
  return false;
371
+ }
311
372
  const details = parent.closest(HTML_DETAILS_TAG);
312
- if (null === details || details.hasAttribute(HTML_DETAILS_OPEN_ATTRIBUTE))
373
+ if (null === details || details.hasAttribute(HTML_DETAILS_OPEN_ATTRIBUTE)) {
313
374
  return false;
375
+ }
314
376
  const summary = Array.from(details.children).find((child) => HTML_SUMMARY_TAG === child.localName);
315
377
  return summary === undefined || !summary.contains(el);
316
378
  }
317
379
  function stalledFade(el) {
318
- if (!el.ownerDocument.hidden || !('getAnimations' in el))
380
+ if (!el.ownerDocument.hidden || !('getAnimations' in el)) {
319
381
  return false;
382
+ }
320
383
  return el.getAnimations().some((a) => 'running' === a.playState || a.pending);
321
384
  }
322
385
  const OVERLAY_ARIA_HIDDEN = 'data-aria-hidden';
323
386
  function hiddenByAnOverlay(el) {
324
387
  return el.hasAttribute(OVERLAY_ARIA_HIDDEN);
325
388
  }
326
- function selfHidden(el, style) {
327
- if ('true' === el.getAttribute('aria-hidden') && !hiddenByAnOverlay(el))
328
- return true;
329
- if (isHtmlElement(el) && el.hidden)
389
+ function selfCssHidden(el, style) {
390
+ if (isHtmlElement(el) && el.hidden) {
330
391
  return true;
331
- if (hiddenInsideClosedDetails(el))
392
+ }
393
+ if (hiddenInsideClosedDetails(el)) {
332
394
  return true;
395
+ }
333
396
  if (style !== null) {
334
397
  if ('none' === style.display ||
335
398
  'hidden' === style.visibility ||
336
399
  'collapse' === style.visibility) {
337
400
  return true;
338
401
  }
339
- if (0 === Number.parseFloat(style.opacity || '1') && !stalledFade(el))
402
+ if (0 === Number.parseFloat(style.opacity || '1') && !stalledFade(el)) {
340
403
  return true;
404
+ }
341
405
  }
342
406
  return false;
343
407
  }
344
- function slotInCapturedClosedRoot(el) {
345
- const host = el.parentElement;
346
- if (null === host || null !== host.shadowRoot)
347
- return null;
348
- const root = capturedRootOf(host);
349
- if (null === root)
350
- return null;
351
- for (const slot of Array.from(root.querySelectorAll('slot'))) {
352
- if (slot.assignedElements().includes(el))
353
- return slot;
408
+ function selfHidden(el, style) {
409
+ if ('true' === el.getAttribute('aria-hidden') && !hiddenByAnOverlay(el)) {
410
+ return true;
354
411
  }
355
- return null;
412
+ return selfCssHidden(el, style);
356
413
  }
357
- function parentAcrossShadowBoundary(el) {
358
- const slot = el.assignedSlot ?? slotInCapturedClosedRoot(el);
359
- if (null !== slot)
360
- return slot;
361
- if (null !== el.parentElement)
362
- return el.parentElement;
363
- const host = el.getRootNode().host;
364
- return host ?? null;
414
+ export function isHiddenByAriaOnly(el) {
415
+ let foundAriaHidden = false;
416
+ let current = el;
417
+ while (null !== current) {
418
+ if ('true' === current.getAttribute('aria-hidden')) {
419
+ foundAriaHidden = true;
420
+ }
421
+ const style = current.ownerDocument.defaultView?.getComputedStyle(current) ?? null;
422
+ if (selfCssHidden(current, style)) {
423
+ return false;
424
+ }
425
+ current = parentAcrossShadowBoundary(current);
426
+ }
427
+ return foundAriaHidden;
428
+ }
429
+ export function isRenderedOutsideAriaHidden(el) {
430
+ let current = el;
431
+ while (null !== current) {
432
+ if ('true' === current.getAttribute('aria-hidden')) {
433
+ return false;
434
+ }
435
+ const style = current.ownerDocument.defaultView?.getComputedStyle(current) ?? null;
436
+ if (selfCssHidden(current, style)) {
437
+ return false;
438
+ }
439
+ current = parentAcrossShadowBoundary(current);
440
+ }
441
+ return true;
365
442
  }
366
443
  const CLIPPING_OVERFLOW = new Set(['hidden', 'clip']);
367
444
  const SCROLLING_OVERFLOW = new Set(['auto', 'scroll']);
@@ -401,8 +478,9 @@ const REPLACED_INLINE_TAGS = new Set([
401
478
  ]);
402
479
  const visibilityInfoMemos = new WeakMap();
403
480
  function visibilityInfoMemo(memo) {
404
- if (memo === undefined)
481
+ if (memo === undefined) {
405
482
  return new Map();
483
+ }
406
484
  let info = visibilityInfoMemos.get(memo);
407
485
  if (info === undefined) {
408
486
  info = new Map();
@@ -454,21 +532,15 @@ function ownVisibilityInfo(el, memo) {
454
532
  }
455
533
  return info;
456
534
  }
457
- function overflowAxes(style) {
458
- const shorthand = style?.overflow ?? '';
459
- const [x = 'visible', y = x] = ('' === shorthand ? 'visible' : shorthand).split(/\s+/);
460
- return [
461
- null === style || '' === style.overflowX ? x : style.overflowX,
462
- null === style || '' === style.overflowY ? y : style.overflowY,
463
- ];
464
- }
465
535
  function positionedContainingBlock(el, fixed, memo) {
466
536
  for (let parent = ownVisibilityInfo(el, memo).parent; parent !== null; parent = ownVisibilityInfo(parent, memo).parent) {
467
537
  const style = ownVisibilityInfo(parent, memo).style;
468
- if (null === style || 'contents' === style.display)
538
+ if (null === style || 'contents' === style.display) {
469
539
  continue;
470
- if (!fixed && '' !== style.position && 'static' !== style.position)
540
+ }
541
+ if (!fixed && '' !== style.position && 'static' !== style.position) {
471
542
  return parent;
543
+ }
472
544
  const transformable = !('inline' === style.display &&
473
545
  isHtmlElement(parent) &&
474
546
  !REPLACED_INLINE_TAGS.has(parent.localName)) &&
@@ -485,92 +557,30 @@ function positionedContainingBlock(el, fixed, memo) {
485
557
  (transformable &&
486
558
  !NON_CLIPPING_DISPLAY.has(style.display) &&
487
559
  (style.contain.split(/\s+/).some((value) => CONTAINING_BLOCK_CONTAIN.has(value)) ||
488
- 'auto' === style.contentVisibility)))
560
+ 'auto' === style.contentVisibility))) {
489
561
  return parent;
562
+ }
490
563
  }
491
564
  return null;
492
565
  }
493
- function clippingBox(el, info) {
494
- if (info.clipBox !== undefined)
495
- return info.clipBox;
496
- const rect = el.getBoundingClientRect();
497
- const style = info.style;
498
- const px = (value) => Number.parseFloat(value ?? '') || 0;
499
- const border = (value, kind) => kind === undefined || '' === kind || 'none' === kind || 'hidden' === kind ? 0 : px(value);
500
- const scaleX = isHtmlElement(el) && el.offsetWidth > 0 ? rect.width / el.offsetWidth : 1;
501
- const scaleY = isHtmlElement(el) && el.offsetHeight > 0 ? rect.height / el.offsetHeight : 1;
502
- const left = isHtmlElement(el) && el.offsetWidth > 0
503
- ? rect.left + el.clientLeft * scaleX
504
- : rect.left + border(style?.borderLeftWidth, style?.borderLeftStyle);
505
- const top = isHtmlElement(el) && el.offsetHeight > 0
506
- ? rect.top + el.clientTop * scaleY
507
- : rect.top + border(style?.borderTopWidth, style?.borderTopStyle);
508
- const right = isHtmlElement(el) && el.offsetWidth > 0
509
- ? left + el.clientWidth * scaleX
510
- : rect.right - border(style?.borderRightWidth, style?.borderRightStyle);
511
- const bottom = isHtmlElement(el) && el.offsetHeight > 0
512
- ? top + el.clientHeight * scaleY
513
- : rect.bottom - border(style?.borderBottomWidth, style?.borderBottomStyle);
514
- const margin = (style?.overflowClipMargin ?? '').split(/\s+/);
515
- const outset = px(margin.at(-1));
516
- const borderBox = margin.includes('border-box');
517
- const contentBox = margin.includes('content-box');
518
- const [x, y] = overflowAxes(style);
519
- const clipLeft = 'clip' === x
520
- ? (borderBox ? rect.left : left + (contentBox ? px(style?.paddingLeft) * scaleX : 0)) -
521
- outset * scaleX
522
- : left;
523
- const clipRight = 'clip' === x
524
- ? (borderBox ? rect.right : right - (contentBox ? px(style?.paddingRight) * scaleX : 0)) +
525
- outset * scaleX
526
- : right;
527
- const clipTop = 'clip' === y
528
- ? (borderBox ? rect.top : top + (contentBox ? px(style?.paddingTop) * scaleY : 0)) -
529
- outset * scaleY
530
- : top;
531
- const clipBottom = 'clip' === y
532
- ? (borderBox ? rect.bottom : bottom - (contentBox ? px(style?.paddingBottom) * scaleY : 0)) +
533
- outset * scaleY
534
- : bottom;
535
- info.clipBox = {
536
- left: clipLeft,
537
- right: clipRight,
538
- top: clipTop,
539
- bottom: clipBottom,
540
- width: clipRight - clipLeft,
541
- height: clipBottom - clipTop,
542
- };
543
- return info.clipBox;
544
- }
545
- function scrolledInto(b, port, alongX, alongY) {
546
- const axis = (lo, hi, size, from, to) => {
547
- if (hi > from && lo < to)
548
- return [lo, hi];
549
- const span = Math.min(size, to - from);
550
- return lo >= to ? [to - span, to] : [from, from + span];
551
- };
552
- const [left, right] = alongX
553
- ? axis(b.left, b.right, b.width, port.left, port.right)
554
- : [b.left, b.right];
555
- const [top, bottom] = alongY
556
- ? axis(b.top, b.bottom, b.height, port.top, port.bottom)
557
- : [b.top, b.bottom];
558
- return { left, right, top, bottom, width: right - left, height: bottom - top };
559
- }
560
566
  function visibleBounds(el, memo, initial, throughScrollers = false) {
561
567
  let bounds = initial;
562
568
  const target = ownVisibilityInfo(el, memo);
563
- if (target.hidden)
569
+ if (target.hidden) {
564
570
  return null;
565
- if (null === target.parent || !ownVisibilityInfo(target.parent, memo).hasClipping)
571
+ }
572
+ if (null === target.parent || !ownVisibilityInfo(target.parent, memo).hasClipping) {
566
573
  return bounds;
574
+ }
567
575
  let clipFrom;
568
576
  for (let current = el; current !== null; current = ownVisibilityInfo(current, memo).parent) {
569
577
  const info = ownVisibilityInfo(current, memo);
570
- if (info.hidden)
578
+ if (info.hidden) {
571
579
  return null;
572
- if (current === clipFrom)
580
+ }
581
+ if (current === clipFrom) {
573
582
  clipFrom = undefined;
583
+ }
574
584
  const style = info.style;
575
585
  if (throughScrollers &&
576
586
  current !== el &&
@@ -589,8 +599,9 @@ function visibleBounds(el, memo, initial, throughScrollers = false) {
589
599
  const right = info.clipX ? Math.min(bounds.right, clip.right) : bounds.right;
590
600
  const top = info.clipY ? Math.max(bounds.top, clip.top) : bounds.top;
591
601
  const bottom = info.clipY ? Math.min(bounds.bottom, clip.bottom) : bounds.bottom;
592
- if (right <= left || bottom <= top)
602
+ if (right <= left || bottom <= top) {
593
603
  return null;
604
+ }
594
605
  bounds = { left, right, top, bottom, width: right - left, height: bottom - top };
595
606
  }
596
607
  }
@@ -601,27 +612,44 @@ function visibleBounds(el, memo, initial, throughScrollers = false) {
601
612
  return bounds;
602
613
  }
603
614
  export function isInViewport(el, memo) {
604
- if (!isVisible(el, memo))
615
+ if (!isVisible(el, memo)) {
605
616
  return false;
617
+ }
606
618
  const view = el.ownerDocument.defaultView;
607
- if (null === view)
619
+ if (null === view) {
608
620
  return false;
621
+ }
609
622
  const r = visibleBounds(el, visibilityInfoMemo(memo), el.getBoundingClientRect());
610
- if (null === r || undefined === r)
623
+ if (null === r || undefined === r) {
611
624
  return false;
612
- if (r.width <= 0 || r.height <= 0)
625
+ }
626
+ if (r.width <= 0 || r.height <= 0) {
613
627
  return false;
628
+ }
614
629
  return r.bottom > 0 && r.right > 0 && r.top < view.innerHeight && r.left < view.innerWidth;
615
630
  }
631
+ function collapsed(el, memo) {
632
+ const own = ownVisibilityInfo(el, memo);
633
+ if (!own.clipX && !own.clipY) {
634
+ return false;
635
+ }
636
+ const rects = Array.from(el.getClientRects());
637
+ return (rects.length > 0 &&
638
+ rects.every((r) => (own.clipX && 0 === r.width) || (own.clipY && 0 === r.height)));
639
+ }
616
640
  export function isVisible(el, memo) {
617
- if (!el.isConnected)
641
+ if (!el.isConnected) {
618
642
  return false;
643
+ }
619
644
  const cached = memo?.get(el);
620
- if (cached !== undefined)
645
+ if (cached !== undefined) {
621
646
  return cached;
622
- const result = visibleBounds(el, visibilityInfoMemo(memo), undefined, true) !== null;
623
- if (memo !== undefined)
647
+ }
648
+ const info = visibilityInfoMemo(memo);
649
+ const result = visibleBounds(el, info, undefined, true) !== null && !collapsed(el, info);
650
+ if (memo !== undefined) {
624
651
  memo.set(el, result);
652
+ }
625
653
  return result;
626
654
  }
627
655
  const MAX_TEXT = 80;
@@ -641,17 +669,21 @@ export function describe(el, memo) {
641
669
  states: getStates(el, visible),
642
670
  visible,
643
671
  };
644
- if (value !== undefined && value.length > 0)
672
+ if (value !== undefined && value.length > 0) {
645
673
  base.value = value;
646
- if (text.length > 0 && text !== name)
674
+ }
675
+ if (text.length > 0 && text !== name) {
647
676
  base.text = text;
677
+ }
648
678
  const source = formatSource(sourceFromDom(el));
649
- if (source !== undefined)
679
+ if (source !== undefined) {
650
680
  base.source = source;
681
+ }
651
682
  if (el.querySelector('path, polyline, polygon') !== null) {
652
683
  const faults = inspectChart(el).findings;
653
- if (faults.length > 0)
684
+ if (faults.length > 0) {
654
685
  base.chart = faults;
686
+ }
655
687
  }
656
688
  return base;
657
689
  }
@@ -17,7 +17,7 @@ export interface CanvasChartData {
17
17
  library: 'chartjs' | 'echarts';
18
18
  data: unknown;
19
19
  }
20
- export declare function canvasChartData(canvas: Element, globals: {
20
+ export interface ChartGlobals {
21
21
  Chart?: {
22
22
  getChart?: (el: Element) => {
23
23
  data?: unknown;
@@ -28,4 +28,5 @@ export declare function canvasChartData(canvas: Element, globals: {
28
28
  getOption?: () => unknown;
29
29
  } | undefined;
30
30
  };
31
- }): CanvasChartData | null;
31
+ }
32
+ export declare function canvasChartData(canvas: Element, globals: ChartGlobals | Window): CanvasChartData | null;
package/dist/dom/chart.js CHANGED
@@ -46,11 +46,12 @@ export function inspectChart(root) {
46
46
  return { examined, findings, canvas };
47
47
  }
48
48
  export function canvasChartData(canvas, globals) {
49
- const chartjs = globals.Chart?.getChart?.(canvas);
49
+ const libs = globals;
50
+ const chartjs = libs.Chart?.getChart?.(canvas);
50
51
  if (chartjs !== undefined && chartjs !== null) {
51
52
  return { library: 'chartjs', data: chartjs.data ?? null };
52
53
  }
53
- const echart = globals.echarts?.getInstanceByDom?.(canvas);
54
+ const echart = libs.echarts?.getInstanceByDom?.(canvas);
54
55
  if (echart !== undefined && echart !== null) {
55
56
  return { library: 'echarts', data: echart.getOption?.() ?? null };
56
57
  }
@@ -7,4 +7,10 @@ export declare function isReticleOverlay(el: Element): boolean;
7
7
  export declare function isUnobserved(el: Element): boolean;
8
8
  export declare function isReticleUi(node: Element | null): boolean;
9
9
  export declare function isIgnored(el: Element): boolean;
10
+ export declare function isWordsIgnored(el: Element): boolean;
11
+ export declare const FrameworkErrorDialog: {
12
+ readonly NEXT: "Next.js error dialog";
13
+ readonly VITE: "Vite error overlay";
14
+ };
15
+ export declare function dialogCovering(matches: readonly Element[]): string | undefined;
10
16
  export declare function appModalOpen(doc: Document): boolean;