@ubio/webvision 2.6.6 → 3.0.1

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/build/global.js CHANGED
@@ -21,7 +21,6 @@ var WebVision = (() => {
21
21
  // src/page/index.ts
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
- Counter: () => Counter,
25
24
  INTERACTIVE_ROLES: () => INTERACTIVE_ROLES,
26
25
  INTERACTIVE_TAGS: () => INTERACTIVE_TAGS,
27
26
  PointSet: () => PointSet,
@@ -44,6 +43,7 @@ var WebVision = (() => {
44
43
  containsSelector: () => containsSelector,
45
44
  escapeAttribute: () => escapeAttribute,
46
45
  fixZIndex: () => fixZIndex,
46
+ getElementDepth: () => getElementDepth,
47
47
  getNormalizedText: () => getNormalizedText,
48
48
  getOffsetTop: () => getOffsetTop,
49
49
  getOverlayContainer: () => getOverlayContainer,
@@ -59,30 +59,18 @@ var WebVision = (() => {
59
59
  isRandomIdentifier: () => isRandomIdentifier,
60
60
  isRectInViewport: () => isRectInViewport,
61
61
  makeOverlaysOpaque: () => makeOverlaysOpaque,
62
+ normalizeRef: () => normalizeRef,
62
63
  normalizeText: () => normalizeText,
63
64
  probeViewport: () => probeViewport,
64
65
  renderVxNode: () => renderVxNode,
65
66
  resolveDomNode: () => resolveDomNode,
67
+ serializeElement: () => serializeElement,
66
68
  showPoint: () => showPoint,
67
69
  traverseElements: () => traverseElements,
68
70
  traverseVxNode: () => traverseVxNode,
69
71
  truncateAttrValue: () => truncateAttrValue
70
72
  });
71
73
 
72
- // src/page/counter.ts
73
- var Counter = class {
74
- constructor(value = 0) {
75
- this.value = value;
76
- }
77
- next() {
78
- this.value += 1;
79
- return this.value;
80
- }
81
- current() {
82
- return this.value;
83
- }
84
- };
85
-
86
74
  // src/page/dom.ts
87
75
  var ORIGINAL_STYLE_SYMBOL = Symbol("vx:originalStyle");
88
76
  var INTERACTIVE_TAGS = ["a", "button", "input", "textarea", "select", "label", "option", "optgroup"];
@@ -269,6 +257,24 @@ var WebVision = (() => {
269
257
  }
270
258
  }
271
259
  }
260
+ function getElementDepth(el) {
261
+ let depth = 0;
262
+ while (el.parentElement) {
263
+ el = el.parentElement;
264
+ depth += 1;
265
+ }
266
+ return depth;
267
+ }
268
+ function serializeElement(el) {
269
+ const sig = [];
270
+ const depth = getElementDepth(el);
271
+ sig.push(String(depth));
272
+ sig.push(el.tagName);
273
+ for (const attr of el.attributes) {
274
+ sig.push(`${attr.name}=${attr.value}`);
275
+ }
276
+ return sig.join("|");
277
+ }
272
278
 
273
279
  // src/page/traverse.ts
274
280
  function* traverseVxNode(vxNode, depth = 0) {
@@ -350,6 +356,191 @@ var WebVision = (() => {
350
356
  function isObjectEmpty(obj) {
351
357
  return !Object.values(obj).some((value) => !!value);
352
358
  }
359
+ function normalizeRef(ref) {
360
+ return ref.toLowerCase().trim().replace(/^[@#]/, "");
361
+ }
362
+
363
+ // src/page/render.ts
364
+ function renderVxNode(scope, options = {}) {
365
+ const buffer = [];
366
+ for (const { vxNode, depth } of traverseVxNode(scope)) {
367
+ if (options.skipNonInteractive && !vxNode.isInteractive) {
368
+ continue;
369
+ }
370
+ const indent = options.skipNonInteractive ? "" : " ".repeat(depth);
371
+ buffer.push(renderIndentedLine(indent, vxNode, options));
372
+ }
373
+ return buffer.join("\n");
374
+ }
375
+ function renderIndentedLine(indent, vxNode, options = {}) {
376
+ const diffPrefix = options.renderDiff ? vxNode.isNew ? "+ " : " " : "";
377
+ if (!vxNode.tagName) {
378
+ return [diffPrefix, indent, vxNode.textContent].filter(Boolean).join("");
379
+ }
380
+ const tagLine = renderTagLine(vxNode, options);
381
+ const htmlStyle = options.renderStyle === "html";
382
+ return [
383
+ diffPrefix,
384
+ indent,
385
+ tagLine,
386
+ htmlStyle ? "" : " ",
387
+ vxNode.textContent ?? ""
388
+ ].join("");
389
+ }
390
+ function renderTagLine(vxNode, options) {
391
+ const htmlStyle = options.renderStyle === "html";
392
+ const components = [];
393
+ if (options.renderTagNames && vxNode.tagName) {
394
+ components.push(vxNode.tagName);
395
+ }
396
+ if (options.renderIds && vxNode.id) {
397
+ if (htmlStyle) {
398
+ components.push(`id="${vxNode.id}"`);
399
+ } else {
400
+ components.push(`#${vxNode.id}`);
401
+ }
402
+ }
403
+ if (options.renderClassNames && vxNode.classList?.length) {
404
+ if (htmlStyle) {
405
+ components.push(`class="${vxNode.classList.join(" ")}"`);
406
+ } else {
407
+ components.push("." + vxNode.classList.join("."));
408
+ }
409
+ }
410
+ if (options.renderRefs) {
411
+ const isRenderRef = [
412
+ options.renderRefs === "all",
413
+ options.renderRefs === true && !isContainerNode(vxNode)
414
+ ].some(Boolean);
415
+ if (isRenderRef) {
416
+ components.push(`[@${vxNode.ref}]`);
417
+ }
418
+ }
419
+ const attrs = [];
420
+ if (options.renderLabelAttrs) {
421
+ for (const [attr, value] of Object.entries(vxNode.labelAttrs ?? {})) {
422
+ attrs.push(`${attr}="${truncateAttrValue(value)}"`);
423
+ }
424
+ }
425
+ if (options.renderValueAttrs) {
426
+ for (const [attr, value] of Object.entries(vxNode.valueAttrs ?? {})) {
427
+ attrs.push(`${attr}="${truncateAttrValue(value)}"`);
428
+ }
429
+ }
430
+ if (options.renderSrcAttrs) {
431
+ for (const [attr, value] of Object.entries(vxNode.srcAttrs ?? {})) {
432
+ attrs.push(`${attr}="${truncateAttrValue(value)}"`);
433
+ }
434
+ }
435
+ if (htmlStyle) {
436
+ components.push(...attrs);
437
+ } else {
438
+ components.push(...attrs.map((attr) => `[${attr}]`));
439
+ }
440
+ return htmlStyle ? `<${components.join(" ")}>` : components.join("");
441
+ }
442
+
443
+ // src/page/snapshot.ts
444
+ var VX_DOM_SYMBOL = Symbol("vx:dom");
445
+ var VX_TREE_SYMBOL = Symbol("vx:tree");
446
+ var VX_LAST_REFS_SYMBOL = Symbol("vx:lastRefs");
447
+ async function captureSnapshot(options = {}) {
448
+ const parser = new VxTreeParser(options);
449
+ const domMap = parser.getDomMap();
450
+ const vxTree = parser.getTree(options.frameId ?? "0", options.iframeRef);
451
+ globalThis[VX_DOM_SYMBOL] = domMap;
452
+ globalThis[VX_TREE_SYMBOL] = vxTree;
453
+ return vxTree;
454
+ }
455
+ function getSnapshot() {
456
+ const vxTree = globalThis[VX_TREE_SYMBOL];
457
+ if (!vxTree) {
458
+ throw new Error("[VX] Snapshot not found");
459
+ }
460
+ return vxTree;
461
+ }
462
+ function resolveDomNode(ref) {
463
+ const domMap = globalThis[VX_DOM_SYMBOL];
464
+ if (!domMap) {
465
+ return null;
466
+ }
467
+ return domMap.get(ref) ?? null;
468
+ }
469
+
470
+ // src/page/tree.ts
471
+ var VxTreeView = class {
472
+ constructor(nodes, frameId, iframeRef) {
473
+ this.nodes = nodes;
474
+ this.frameId = frameId;
475
+ this.iframeRef = iframeRef;
476
+ this.refMap = /* @__PURE__ */ new Map();
477
+ this.buildRefMap(nodes);
478
+ }
479
+ get nodeCount() {
480
+ return this.refMap.size;
481
+ }
482
+ *traverse() {
483
+ for (const vxNode of this.nodes) {
484
+ yield* traverseVxNode(vxNode, 0);
485
+ }
486
+ }
487
+ buildRefMap(nodes) {
488
+ for (const node of nodes) {
489
+ if (node.ref) {
490
+ this.refMap.set(node.ref, node);
491
+ }
492
+ this.buildRefMap(node.children ?? []);
493
+ }
494
+ }
495
+ hasRef(ref) {
496
+ const nref = normalizeRef(ref);
497
+ return this.refMap.has(nref);
498
+ }
499
+ findNode(ref) {
500
+ const nref = normalizeRef(ref);
501
+ return this.refMap.get(nref) ?? null;
502
+ }
503
+ render(options = {}) {
504
+ return this.nodes.map((node) => {
505
+ return renderVxNode(node, options);
506
+ }).join("\n");
507
+ }
508
+ highlight(options = {}) {
509
+ if (options.clearOverlay) {
510
+ clearOverlay();
511
+ }
512
+ const leafOnly = !options.includeAll;
513
+ const refs = this.collectRefs(leafOnly, options.filterRefs);
514
+ this.highlightRefs(refs, options);
515
+ }
516
+ highlightRefs(refs, options = {}) {
517
+ for (const ref of refs) {
518
+ const el = resolveDomNode(ref);
519
+ if (el instanceof Element) {
520
+ const vxNode = this.findNode(ref);
521
+ const isNew = vxNode?.isNew ?? false;
522
+ const color = options.useColors ? void 0 : isNew ? "#0a0" : "#060";
523
+ highlightEl(el, ref, color);
524
+ }
525
+ }
526
+ }
527
+ collectRefs(leafOnly = true, filterRefs) {
528
+ const refs = [];
529
+ for (const { vxNode } of this.traverse()) {
530
+ if (!vxNode.ref) {
531
+ continue;
532
+ }
533
+ if (leafOnly && isContainerNode(vxNode)) {
534
+ continue;
535
+ }
536
+ if (filterRefs && !filterRefs.includes(vxNode.ref)) {
537
+ continue;
538
+ }
539
+ refs.push(vxNode.ref);
540
+ }
541
+ return refs;
542
+ }
543
+ };
353
544
 
354
545
  // src/page/parser.ts
355
546
  var VX_NODE_SYMBOL = Symbol("vx:node");
@@ -422,41 +613,36 @@ var WebVision = (() => {
422
613
  var VxTreeParser = class {
423
614
  constructor(options = {}) {
424
615
  this.options = options;
616
+ this.viewport = { width: 0, height: 0 };
617
+ this.vxNodes = [];
425
618
  this.probeElements = [];
426
619
  this.domRefMap = /* @__PURE__ */ new Map();
427
- const { startRef = 0 } = options;
620
+ }
621
+ async parse() {
428
622
  this.viewport = getViewportSize();
429
- this.counter = new Counter(startRef);
430
- if (options.probeViewport) {
623
+ if (this.options.probeViewport) {
431
624
  this.probeElements = probeViewport();
432
625
  }
433
- const vxRoot = this.parseDocument();
434
- this.refRange = [startRef, this.counter.current()];
626
+ const vxRoot = await this.parseNode(document.documentElement, null);
435
627
  this.vxNodes = this.pruneRecursive(vxRoot);
436
628
  }
437
- parseDocument() {
438
- return this.parseNode(document.documentElement, null);
439
- }
440
- getTree() {
441
- return new VxTreeView(this.vxNodes, this.refRange);
629
+ getTree(frameId, iframeRef) {
630
+ return new VxTreeView(this.vxNodes, frameId, iframeRef);
442
631
  }
443
632
  getNodes() {
444
633
  return this.vxNodes;
445
634
  }
446
- getRefRange() {
447
- return this.refRange;
448
- }
449
635
  getDomMap() {
450
636
  return this.domRefMap;
451
637
  }
452
- parseNode(node, parent) {
638
+ async parseNode(node, parent) {
453
639
  if (!node || node[VX_IGNORE_SYMBOL]) {
454
640
  return null;
455
641
  }
456
642
  if (node instanceof Text) {
457
643
  const textContent = normalizeText(node.textContent ?? "");
458
644
  if (textContent) {
459
- return this.makeNode(node, {
645
+ return await this.makeNode(node, {
460
646
  textContent,
461
647
  hasVisibleArea: parent?.hasVisibleArea,
462
648
  isOutsideViewport: parent?.isOutsideViewport,
@@ -469,11 +655,11 @@ var WebVision = (() => {
469
655
  if (this.options.opaqueOverlays) {
470
656
  makeOverlaysOpaque(node);
471
657
  }
472
- return this.parseElement(node);
658
+ return await this.parseElement(node);
473
659
  }
474
660
  return null;
475
661
  }
476
- parseElement(el) {
662
+ async parseElement(el) {
477
663
  const skip = VX_IGNORE_TAGS.includes(el.tagName.toLowerCase()) || isHidden(el);
478
664
  if (skip) {
479
665
  this.clearRecursive(el);
@@ -482,7 +668,7 @@ var WebVision = (() => {
482
668
  const parentEl = el.matches("option, optgroup") ? el.closest("select") ?? el : el;
483
669
  const rect = parentEl.getBoundingClientRect();
484
670
  const id = el.getAttribute("id") ?? "";
485
- const vxNode = this.makeNode(el, {
671
+ const vxNode = await this.makeNode(el, {
486
672
  tagName: el.tagName.toLowerCase(),
487
673
  id: isRandomIdentifier(id) ? void 0 : id,
488
674
  classList: Array.from(el.classList).filter((cls) => !isRandomIdentifier(cls)).slice(0, 4),
@@ -495,7 +681,13 @@ var WebVision = (() => {
495
681
  isProbeHit: this.isProbeHit(parentEl),
496
682
  isKept: el.matches(VX_KEEP_SELECTOR)
497
683
  });
498
- const children = [...el.childNodes].map((child) => this.parseNode(child, vxNode)).filter((_) => _ != null);
684
+ const children = [];
685
+ for (const child of el.childNodes) {
686
+ const childNode = await this.parseNode(child, vxNode);
687
+ if (childNode != null) {
688
+ children.push(childNode);
689
+ }
690
+ }
499
691
  if (children.length === 1 && !children[0]?.tagName) {
500
692
  vxNode.textContent = normalizeText(children[0].textContent ?? "");
501
693
  } else {
@@ -503,15 +695,17 @@ var WebVision = (() => {
503
695
  }
504
696
  return vxNode;
505
697
  }
506
- makeNode(node, spec) {
507
- const ref = this.counter.next();
698
+ async makeNode(node, spec) {
508
699
  const isNew = !node[VX_NODE_SYMBOL];
700
+ const ref = await this.createRef(node);
509
701
  const vxNode = {
510
702
  ...spec,
511
703
  ref,
512
704
  isNew
513
705
  };
514
- this.domRefMap.set(ref, node);
706
+ if (ref) {
707
+ this.domRefMap.set(ref, node);
708
+ }
515
709
  node[VX_NODE_SYMBOL] = vxNode;
516
710
  return vxNode;
517
711
  }
@@ -649,6 +843,13 @@ var WebVision = (() => {
649
843
  }
650
844
  }
651
845
  }
846
+ async createRef(node) {
847
+ if (node instanceof Element) {
848
+ const sig = serializeElement(node);
849
+ const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(sig).buffer);
850
+ return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join("").substring(0, 8);
851
+ }
852
+ }
652
853
  };
653
854
 
654
855
  // src/page/overlay.ts
@@ -692,7 +893,7 @@ var WebVision = (() => {
692
893
  }
693
894
 
694
895
  // src/page/highlight.ts
695
- function highlightEl(el, ref = 0, color) {
896
+ function highlightEl(el, ref, color) {
696
897
  if (!(el instanceof Element)) {
697
898
  return;
698
899
  }
@@ -728,191 +929,18 @@ var WebVision = (() => {
728
929
  label.style.transform = "translateY(50%)";
729
930
  label.textContent = String(ref);
730
931
  }
731
- function getRandomColor(index) {
932
+ function getRandomColor(ref) {
933
+ const index = ref.charCodeAt(0);
732
934
  const hue = index * 120 * 0.382 % 360;
733
935
  return `hsl(${hue}, 85%, 50%)`;
734
936
  }
735
937
 
736
- // src/page/render.ts
737
- function renderVxNode(scope, options = {}) {
738
- const buffer = [];
739
- for (const { vxNode, depth } of traverseVxNode(scope)) {
740
- if (options.skipNonInteractive && !vxNode.isInteractive) {
741
- continue;
742
- }
743
- const indent = options.skipNonInteractive ? "" : " ".repeat(depth);
744
- buffer.push(renderIndentedLine(indent, vxNode, options));
745
- }
746
- return buffer.join("\n");
747
- }
748
- function renderIndentedLine(indent, vxNode, options = {}) {
749
- const diffPrefix = options.renderDiff ? vxNode.isNew ? "+ " : " " : "";
750
- if (!vxNode.tagName) {
751
- return [diffPrefix, indent, vxNode.textContent].filter(Boolean).join("");
752
- }
753
- const tagLine = renderTagLine(vxNode, options);
754
- const htmlStyle = options.renderStyle === "html";
755
- return [
756
- diffPrefix,
757
- indent,
758
- tagLine,
759
- htmlStyle ? "" : " ",
760
- vxNode.textContent ?? ""
761
- ].join("");
762
- }
763
- function renderTagLine(vxNode, options) {
764
- const htmlStyle = options.renderStyle === "html";
765
- const components = [];
766
- if (options.renderTagNames && vxNode.tagName) {
767
- components.push(vxNode.tagName);
768
- }
769
- if (options.renderIds && vxNode.id) {
770
- if (htmlStyle) {
771
- components.push(`id="${vxNode.id}"`);
772
- } else {
773
- components.push(`#${vxNode.id}`);
774
- }
775
- }
776
- if (options.renderClassNames && vxNode.classList?.length) {
777
- if (htmlStyle) {
778
- components.push(`class="${vxNode.classList.join(" ")}"`);
779
- } else {
780
- components.push("." + vxNode.classList.join("."));
781
- }
782
- }
783
- if (options.renderRefs) {
784
- const isRenderRef = [
785
- options.renderRefs === "all",
786
- options.renderRefs === true && !isContainerNode(vxNode)
787
- ].some(Boolean);
788
- if (isRenderRef) {
789
- components.push(`[@${vxNode.ref}]`);
790
- }
791
- }
792
- const attrs = [];
793
- if (options.renderLabelAttrs) {
794
- for (const [attr, value] of Object.entries(vxNode.labelAttrs ?? {})) {
795
- attrs.push(`${attr}="${truncateAttrValue(value)}"`);
796
- }
797
- }
798
- if (options.renderValueAttrs) {
799
- for (const [attr, value] of Object.entries(vxNode.valueAttrs ?? {})) {
800
- attrs.push(`${attr}="${truncateAttrValue(value)}"`);
801
- }
802
- }
803
- if (options.renderSrcAttrs) {
804
- for (const [attr, value] of Object.entries(vxNode.srcAttrs ?? {})) {
805
- attrs.push(`${attr}="${truncateAttrValue(value)}"`);
806
- }
807
- }
808
- if (htmlStyle) {
809
- components.push(...attrs);
810
- } else {
811
- components.push(...attrs.map((attr) => `[${attr}]`));
812
- }
813
- return htmlStyle ? `<${components.join(" ")}>` : components.join("");
814
- }
815
-
816
- // src/page/snapshot.ts
817
- var VX_DOM_SYMBOL = Symbol("vx:dom");
818
- var VX_TREE_SYMBOL = Symbol("vx:tree");
819
- var VX_LAST_REFS_SYMBOL = Symbol("vx:lastRefs");
820
- function captureSnapshot(options = {}) {
821
- const parser = new VxTreeParser(options);
822
- const domMap = parser.getDomMap();
823
- const vxTree = parser.getTree();
824
- globalThis[VX_DOM_SYMBOL] = domMap;
825
- globalThis[VX_TREE_SYMBOL] = vxTree;
826
- return vxTree;
827
- }
828
- function getSnapshot() {
829
- const vxTree = globalThis[VX_TREE_SYMBOL];
830
- if (!vxTree) {
831
- throw new Error("[VX] Snapshot not found");
832
- }
833
- return vxTree;
834
- }
835
- function resolveDomNode(ref) {
836
- const domMap = globalThis[VX_DOM_SYMBOL];
837
- if (!domMap) {
838
- return null;
839
- }
840
- return domMap.get(ref) ?? null;
841
- }
842
-
843
- // src/page/tree.ts
844
- var VxTreeView = class {
845
- constructor(nodes, refRange) {
846
- this.nodes = nodes;
847
- this.refRange = refRange;
848
- this.refMap = /* @__PURE__ */ new Map();
849
- this.buildRefMap(nodes);
850
- }
851
- get nodeCount() {
852
- return this.refMap.size;
853
- }
854
- *traverse() {
855
- for (const vxNode of this.nodes) {
856
- yield* traverseVxNode(vxNode, 0);
857
- }
858
- }
859
- buildRefMap(nodes) {
860
- for (const node of nodes) {
861
- this.refMap.set(node.ref, node);
862
- this.buildRefMap(node.children ?? []);
863
- }
864
- }
865
- findNode(ref) {
866
- return this.refMap.get(ref) ?? null;
867
- }
868
- render(options = {}) {
869
- return this.nodes.map((node) => {
870
- return renderVxNode(node, options);
871
- }).join("\n");
872
- }
873
- highlight(options = {}) {
874
- if (options.clearOverlay) {
875
- clearOverlay();
876
- }
877
- const leafOnly = !options.includeAll;
878
- const refs = this.collectRefs(leafOnly, options.filterRefs);
879
- this.highlightRefs(refs, options);
880
- }
881
- highlightRefs(refs, options = {}) {
882
- for (const ref of refs) {
883
- const el = resolveDomNode(ref);
884
- if (el instanceof Element) {
885
- const vxNode = this.findNode(ref);
886
- const isNew = vxNode?.isNew ?? false;
887
- const color = options.useColors ? void 0 : isNew ? "#0a0" : "#060";
888
- highlightEl(el, ref, color);
889
- }
890
- }
891
- }
892
- collectRefs(leafOnly = true, filterRefs) {
893
- const refs = [];
894
- for (const { vxNode } of this.traverse()) {
895
- if (leafOnly && isContainerNode(vxNode)) {
896
- continue;
897
- }
898
- if (filterRefs && !filterRefs.includes(vxNode.ref)) {
899
- continue;
900
- }
901
- refs.push(vxNode.ref);
902
- }
903
- return refs;
904
- }
905
- };
906
-
907
- // src/page/frame.ts
938
+ // src/page/page.ts
908
939
  var VxPageView = class {
909
- constructor(vxFrames) {
910
- this.vxFrames = vxFrames;
911
- this.vxFrameMap = /* @__PURE__ */ new Map();
940
+ constructor(vxTrees) {
912
941
  this.vxTreeMap = /* @__PURE__ */ new Map();
913
- for (const frame of vxFrames) {
914
- this.vxFrameMap.set(frame.frameId, frame);
915
- this.vxTreeMap.set(frame.frameId, new VxTreeView(frame.nodes, frame.refRange));
942
+ for (const vxTree of vxTrees) {
943
+ this.vxTreeMap.set(vxTree.frameId, vxTree);
916
944
  }
917
945
  }
918
946
  get vxTrees() {
@@ -922,43 +950,28 @@ var WebVision = (() => {
922
950
  return this.vxTrees.reduce((acc, vxTree) => acc + vxTree.nodeCount, 0);
923
951
  }
924
952
  findNodeByRef(ref) {
925
- const vxTree = this.findTreeByRef(ref);
926
- if (vxTree == null) {
953
+ const frame = this.findFrameByRef(ref);
954
+ if (frame == null) {
927
955
  return null;
928
956
  }
929
- return vxTree.findNode(ref);
957
+ return frame.findNode(ref);
930
958
  }
931
- findTreeByFrameId(frameId) {
959
+ findFrameById(frameId) {
932
960
  return this.vxTreeMap.get(frameId) ?? null;
933
961
  }
934
- findTreeByRef(ref) {
935
- const vxFrame = this.findFrameByRef(ref);
936
- if (vxFrame == null) {
937
- return null;
938
- }
939
- return this.vxTreeMap.get(vxFrame.frameId) ?? null;
940
- }
941
- findFrameByFrameId(frameId) {
942
- return this.vxFrameMap.get(frameId) ?? null;
943
- }
944
962
  findFrameByRef(ref) {
945
- const vxFrame = this.vxFrames.find(
946
- (frame) => ref >= frame.refRange[0] && ref <= frame.refRange[1]
947
- );
948
- if (!vxFrame) {
949
- return null;
950
- }
951
- return this.vxFrameMap.get(vxFrame.frameId) ?? null;
963
+ const frame = this.vxTrees.find((f) => f.hasRef(ref));
964
+ return frame ?? null;
952
965
  }
953
966
  getFrameByRef(ref) {
954
- const vxFrame = this.findFrameByRef(ref);
955
- if (vxFrame == null) {
967
+ const frame = this.findFrameByRef(ref);
968
+ if (frame == null) {
956
969
  throw new Error(`[VX] Frame not found for [ref=${ref}]`);
957
970
  }
958
- return vxFrame;
971
+ return frame;
959
972
  }
960
973
  findParentFrame(frameId) {
961
- const frame = this.findFrameByFrameId(frameId);
974
+ const frame = this.findFrameById(frameId);
962
975
  const iframeRef = frame?.iframeRef;
963
976
  if (iframeRef == null) {
964
977
  return null;
@@ -966,7 +979,7 @@ var WebVision = (() => {
966
979
  return this.findFrameByRef(iframeRef);
967
980
  }
968
981
  isFrameShown(frameId) {
969
- const frame = this.findFrameByFrameId(frameId);
982
+ const frame = this.findFrameById(frameId);
970
983
  if (!frame) {
971
984
  return false;
972
985
  }
@@ -975,14 +988,12 @@ var WebVision = (() => {
975
988
  if (parentFrame == null || iframeRef == null) {
976
989
  return true;
977
990
  }
978
- const vxTree = this.vxTreeMap.get(frameId);
979
- const existsInParent = !!vxTree?.findNode(iframeRef);
991
+ const existsInParent = !!parentFrame.findNode(iframeRef);
980
992
  return existsInParent ? this.isFrameShown(parentFrame.frameId) : false;
981
993
  }
982
994
  renderAll(options) {
983
- return this.vxFrames.map((frame) => {
984
- const vxTree = this.vxTreeMap.get(frame.frameId);
985
- const rendered = vxTree?.render(options);
995
+ return this.vxTrees.map((frame) => {
996
+ const rendered = frame?.render(options);
986
997
  return [
987
998
  `FRAME ${frame.frameId}${frame.iframeRef ? ` [@${frame.iframeRef}]` : ""}`,
988
999
  rendered