@dotcms/uve 1.5.5-next.2294 → 1.5.5-next.2323

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/internal.cjs.js CHANGED
@@ -399,6 +399,7 @@ exports.getDotCMSContentletsBound = _public.getDotCMSContentletsBound;
399
399
  exports.getDotCMSPageBounds = _public.getDotCMSPageBounds;
400
400
  exports.getDotContainerAttributes = _public.getDotContainerAttributes;
401
401
  exports.getDotContentletAttributes = _public.getDotContentletAttributes;
402
+ exports.getNativeEventBinder = _public.getNativeEventBinder;
402
403
  exports.getUVEState = _public.getUVEState;
403
404
  exports.isDotAnalyticsActive = _public.isDotAnalyticsActive;
404
405
  exports.isValidBlocks = _public.isValidBlocks;
package/internal.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { g as getUVEState, s as sendMessageToUVE } from './public.esm.js';
2
- export { A as ANALYTICS_ACTIVE_WINDOW_KEY, l as ANALYTICS_READY_EVENT, C as CUSTOM_NO_COMPONENT, D as DEVELOPMENT_MODE, k as DOT_SECTION_ID_PREFIX, j as EMPTY_CONTAINER_STYLE_ANGULAR, h as EMPTY_CONTAINER_STYLE_REACT, E as END_CLASS, P as PRODUCTION_MODE, S as START_CLASS, T as TEMP_EMPTY_CONTENTLET, m as TEMP_EMPTY_CONTENTLET_TYPE, _ as __UVE_EVENTS__, f as __UVE_EVENT_ERROR_FALLBACK__, x as combineClasses, w as computeScrollIsInBottom, c as createUVESubscription, t as findDotCMSElement, v as findDotCMSVTLData, B as getAnalyticsContentletAttributes, q as getClosestDotCMSContainerData, y as getColumnPositionClasses, G as getContainersData, H as getContentletsInContainer, p as getDotCMSContainerData, o as getDotCMSContentletsBound, n as getDotCMSPageBounds, I as getDotContainerAttributes, z as getDotContentletAttributes, F as isDotAnalyticsActive, L as isValidBlocks, J as readContentletDataset, K as setBounds } from './public.esm.js';
2
+ export { A as ANALYTICS_ACTIVE_WINDOW_KEY, l as ANALYTICS_READY_EVENT, C as CUSTOM_NO_COMPONENT, D as DEVELOPMENT_MODE, k as DOT_SECTION_ID_PREFIX, j as EMPTY_CONTAINER_STYLE_ANGULAR, h as EMPTY_CONTAINER_STYLE_REACT, E as END_CLASS, P as PRODUCTION_MODE, S as START_CLASS, T as TEMP_EMPTY_CONTENTLET, m as TEMP_EMPTY_CONTENTLET_TYPE, _ as __UVE_EVENTS__, f as __UVE_EVENT_ERROR_FALLBACK__, x as combineClasses, w as computeScrollIsInBottom, c as createUVESubscription, t as findDotCMSElement, v as findDotCMSVTLData, B as getAnalyticsContentletAttributes, q as getClosestDotCMSContainerData, y as getColumnPositionClasses, G as getContainersData, H as getContentletsInContainer, p as getDotCMSContainerData, o as getDotCMSContentletsBound, n as getDotCMSPageBounds, I as getDotContainerAttributes, z as getDotContentletAttributes, K as getNativeEventBinder, F as isDotAnalyticsActive, M as isValidBlocks, J as readContentletDataset, L as setBounds } from './public.esm.js';
3
3
  import { UVE_MODE, DotCMSUVEAction } from '@dotcms/types';
4
4
  import '@dotcms/types/internal';
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotcms/uve",
3
- "version": "1.5.5-next.2294",
3
+ "version": "1.5.5-next.2323",
4
4
  "description": "Official JavaScript library for interacting with Universal Visual Editor (UVE)",
5
5
  "repository": {
6
6
  "type": "git",
package/public.cjs.js CHANGED
@@ -409,6 +409,38 @@ function readContentletDataset(element) {
409
409
  })
410
410
  };
411
411
  }
412
+ /**
413
+ * Returns Zone.js's *unpatched* native `addEventListener` / `removeEventListener`
414
+ * bound to `target`, falling back to the standard methods when Zone.js is absent.
415
+ *
416
+ * UVE reuses a single iframe and rewrites it with `document.open()/write()/close()`
417
+ * on every in-editor navigation. When Zone.js is loaded inside that iframe it runs
418
+ * in global-events mode: one native "gateway" listener per (target, eventType)
419
+ * plus a JS-level task list stored on the target node. `document.open()` tears down
420
+ * the native gateway on the persistent `window`/`document` nodes, but the task list
421
+ * survives on them, so Zone sees "already registered" on re-init and skips
422
+ * re-installing the native gateway — the listener silently goes dead after the
423
+ * first navigation.
424
+ *
425
+ * Hover/click dodge this by binding to `document.documentElement`, a node that
426
+ * `write()` recreates fresh. `scroll`/`message` (on `window`) and
427
+ * `DOMContentLoaded` (on `document`) can't: none of them fire on `<html>`, so
428
+ * there is no fresh node to rebind to. Going through Zone's native (untracked)
429
+ * methods sidesteps the dedup entirely, so the listener rebinds cleanly after
430
+ * every rewrite.
431
+ */
432
+ function getNativeEventBinder(target) {
433
+ const zone = globalThis.Zone;
434
+ const symbolFor = name => typeof zone?.__symbol__ === 'function' ? zone.__symbol__(name) : `__zone_symbol__${name}`;
435
+ const source = target;
436
+ const base = target;
437
+ const nativeAdd = source[symbolFor('addEventListener')];
438
+ const nativeRemove = source[symbolFor('removeEventListener')];
439
+ return {
440
+ addEventListener: (nativeAdd ?? base.addEventListener).bind(base),
441
+ removeEventListener: (nativeRemove ?? base.removeEventListener).bind(base)
442
+ };
443
+ }
412
444
 
413
445
  /**
414
446
  * Subscribes to content changes in the UVE editor
@@ -425,10 +457,12 @@ function onContentChanges(callback) {
425
457
  callback(event.data.payload);
426
458
  }
427
459
  };
428
- window.addEventListener('message', messageCallback);
460
+ // Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
461
+ const nativeWindow = getNativeEventBinder(window);
462
+ nativeWindow.addEventListener('message', messageCallback);
429
463
  return {
430
464
  unsubscribe: () => {
431
- window.removeEventListener('message', messageCallback);
465
+ nativeWindow.removeEventListener('message', messageCallback);
432
466
  },
433
467
  event: types.UVEEventType.CONTENT_CHANGES
434
468
  };
@@ -448,10 +482,12 @@ function onPageReload(callback) {
448
482
  callback();
449
483
  }
450
484
  };
451
- window.addEventListener('message', messageCallback);
485
+ // Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
486
+ const nativeWindow = getNativeEventBinder(window);
487
+ nativeWindow.addEventListener('message', messageCallback);
452
488
  return {
453
489
  unsubscribe: () => {
454
- window.removeEventListener('message', messageCallback);
490
+ nativeWindow.removeEventListener('message', messageCallback);
455
491
  },
456
492
  event: types.UVEEventType.PAGE_RELOAD
457
493
  };
@@ -539,13 +575,13 @@ function onAutoBounds(callback) {
539
575
  childList: true,
540
576
  subtree: true
541
577
  });
542
- // Scrolling inside the iframe doesn't change layout, so ResizeObserver
543
- // doesn't fire, but every contentlet's viewport-relative position
544
- // (getBoundingClientRect) does change. Re-emit bounds after each
545
- // scroll burst settles so the editor's pinned selected overlay
546
- // re-anchors to the on-screen position.
578
+ // Scrolling doesn't change layout (ResizeObserver won't fire) but moves
579
+ // every contentlet, so re-emit bounds after the scroll settles to re-anchor
580
+ // the selected overlay. Native binder so it survives the iframe rewrite
581
+ // under Zone.js. See getNativeEventBinder.
582
+ const nativeWindow = getNativeEventBinder(window);
547
583
  const onScroll = () => scheduleEmit();
548
- window.addEventListener('scroll', onScroll, {
584
+ nativeWindow.addEventListener('scroll', onScroll, {
549
585
  passive: true
550
586
  });
551
587
  // Flush channel: the editor occasionally needs an immediate snapshot
@@ -560,7 +596,8 @@ function onAutoBounds(callback) {
560
596
  }
561
597
  emit();
562
598
  };
563
- window.addEventListener('message', onFlush);
599
+ // Native binder (reused from `scroll` above): survives the iframe rewrite too.
600
+ nativeWindow.addEventListener('message', onFlush);
564
601
  return {
565
602
  unsubscribe: () => {
566
603
  if (debounceTimer !== null) {
@@ -569,8 +606,8 @@ function onAutoBounds(callback) {
569
606
  }
570
607
  resizeObserver.disconnect();
571
608
  mutationObserver.disconnect();
572
- window.removeEventListener('scroll', onScroll);
573
- window.removeEventListener('message', onFlush);
609
+ nativeWindow.removeEventListener('scroll', onScroll);
610
+ nativeWindow.removeEventListener('message', onFlush);
574
611
  observed = [];
575
612
  },
576
613
  event: types.UVEEventType.AUTO_BOUNDS
@@ -592,10 +629,12 @@ function onIframeScroll(callback) {
592
629
  callback(direction);
593
630
  }
594
631
  };
595
- window.addEventListener('message', messageCallback);
632
+ // Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
633
+ const nativeWindow = getNativeEventBinder(window);
634
+ nativeWindow.addEventListener('message', messageCallback);
596
635
  return {
597
636
  unsubscribe: () => {
598
- window.removeEventListener('message', messageCallback);
637
+ nativeWindow.removeEventListener('message', messageCallback);
599
638
  },
600
639
  event: types.UVEEventType.IFRAME_SCROLL
601
640
  };
@@ -625,10 +664,12 @@ function onScrollToSection(callback) {
625
664
  offsetTop: el.offsetTop
626
665
  });
627
666
  };
628
- window.addEventListener('message', messageCallback);
667
+ // Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
668
+ const nativeWindow = getNativeEventBinder(window);
669
+ nativeWindow.addEventListener('message', messageCallback);
629
670
  return {
630
671
  unsubscribe: () => {
631
- window.removeEventListener('message', messageCallback);
672
+ nativeWindow.removeEventListener('message', messageCallback);
632
673
  },
633
674
  event: types.UVEEventType.SCROLL_TO_SECTION
634
675
  };
@@ -698,16 +739,18 @@ function onContentletHovered(callback) {
698
739
  hasHover = true;
699
740
  callback(contentletHoveredPayload);
700
741
  };
701
- // We intentionally do not fire null on document `pointerleave`: the
702
- // editor's hover toolbar lives in the parent window (outside the
703
- // iframe), so leaving the iframe usually means the user is heading
704
- // for the toolbar. Killing the overlay there would yank the toolbar
705
- // away just as the user reaches for it. Dead-space-inside-iframe
706
- // is already covered by the `pointermove` null branch above.
707
- document.addEventListener('pointermove', pointerMoveCallback);
742
+ // We intentionally do not fire null on `pointerleave`: the editor's hover
743
+ // toolbar lives in the parent window, so leaving the iframe usually means
744
+ // the user is reaching for it killing the overlay would yank it away.
745
+ //
746
+ // Bind to `document.documentElement` (a fresh node on each iframe rewrite)
747
+ // rather than `document`, which goes dead under Zone.js after a rewrite.
748
+ // `pointermove` bubbles to <html>, so no-Zone behavior is identical. See
749
+ // getNativeEventBinder for the full rationale.
750
+ document.documentElement.addEventListener('pointermove', pointerMoveCallback);
708
751
  return {
709
752
  unsubscribe: () => {
710
- document.removeEventListener('pointermove', pointerMoveCallback);
753
+ document.documentElement.removeEventListener('pointermove', pointerMoveCallback);
711
754
  },
712
755
  event: types.UVEEventType.CONTENTLET_HOVERED
713
756
  };
@@ -781,18 +824,22 @@ function onContentletClicked(callback) {
781
824
  lastSelectedInode = undefined;
782
825
  }
783
826
  };
784
- // Capture phase so we run BEFORE the page's own click handlers and can
785
- // preventDefault/stopPropagation effectively.
786
- document.addEventListener('click', clickCallback, {
827
+ // Capture phase so we run BEFORE the page's own click handlers. Bound to
828
+ // `document.documentElement` (a fresh node on each iframe rewrite) rather
829
+ // than `document`, which goes dead under Zone.js after a rewrite; capture on
830
+ // <html> still runs first. See getNativeEventBinder for the full rationale.
831
+ document.documentElement.addEventListener('click', clickCallback, {
787
832
  capture: true
788
833
  });
789
- window.addEventListener('message', selectionClearedCallback);
834
+ // Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
835
+ const nativeWindow = getNativeEventBinder(window);
836
+ nativeWindow.addEventListener('message', selectionClearedCallback);
790
837
  return {
791
838
  unsubscribe: () => {
792
- document.removeEventListener('click', clickCallback, {
839
+ document.documentElement.removeEventListener('click', clickCallback, {
793
840
  capture: true
794
841
  });
795
- window.removeEventListener('message', selectionClearedCallback);
842
+ nativeWindow.removeEventListener('message', selectionClearedCallback);
796
843
  },
797
844
  event: types.UVEEventType.CONTENTLET_CLICKED
798
845
  };
@@ -1144,12 +1191,19 @@ function scrollHandler() {
1144
1191
  action: types.DotCMSUVEAction.IFRAME_SCROLL_END
1145
1192
  });
1146
1193
  };
1147
- window.addEventListener('scroll', scrollCallback);
1148
- window.addEventListener('scrollend', scrollEndCallback);
1194
+ // Native binder: scroll survives the iframe rewrite under Zone.js. It can't
1195
+ // move to a fresh `documentElement` like hover/click (viewport scroll only
1196
+ // fires on `window`). See getNativeEventBinder.
1197
+ const {
1198
+ addEventListener,
1199
+ removeEventListener
1200
+ } = getNativeEventBinder(window);
1201
+ addEventListener('scroll', scrollCallback);
1202
+ addEventListener('scrollend', scrollEndCallback);
1149
1203
  return {
1150
1204
  destroyScrollHandler: () => {
1151
- window.removeEventListener('scroll', scrollCallback);
1152
- window.removeEventListener('scrollend', scrollEndCallback);
1205
+ removeEventListener('scroll', scrollCallback);
1206
+ removeEventListener('scrollend', scrollEndCallback);
1153
1207
  }
1154
1208
  };
1155
1209
  }
@@ -1263,14 +1317,19 @@ function listenBlockEditorInlineEvent() {
1263
1317
  }
1264
1318
  };
1265
1319
  }
1266
- // If the page is not fully loaded, listen for the DOMContentLoaded event
1320
+ // Native binder: `DOMContentLoaded` fires on `document` (not `<html>`), so
1321
+ // it survives the iframe rewrite under Zone.js. See getNativeEventBinder.
1267
1322
  const handleDOMContentLoaded = () => {
1268
1323
  listenBlockEditorClick();
1269
1324
  };
1270
- document.addEventListener('DOMContentLoaded', handleDOMContentLoaded);
1325
+ const {
1326
+ addEventListener,
1327
+ removeEventListener
1328
+ } = getNativeEventBinder(document);
1329
+ addEventListener('DOMContentLoaded', handleDOMContentLoaded);
1271
1330
  return {
1272
1331
  destroyListenBlockEditorInlineEvent: () => {
1273
- document.removeEventListener('DOMContentLoaded', handleDOMContentLoaded);
1332
+ removeEventListener('DOMContentLoaded', handleDOMContentLoaded);
1274
1333
  }
1275
1334
  };
1276
1335
  }
@@ -1520,6 +1579,7 @@ exports.getDotCMSContentletsBound = getDotCMSContentletsBound;
1520
1579
  exports.getDotCMSPageBounds = getDotCMSPageBounds;
1521
1580
  exports.getDotContainerAttributes = getDotContainerAttributes;
1522
1581
  exports.getDotContentletAttributes = getDotContentletAttributes;
1582
+ exports.getNativeEventBinder = getNativeEventBinder;
1523
1583
  exports.getUVEState = getUVEState;
1524
1584
  exports.initInlineEditing = initInlineEditing;
1525
1585
  exports.initUVE = initUVE;
package/public.esm.js CHANGED
@@ -407,6 +407,38 @@ function readContentletDataset(element) {
407
407
  })
408
408
  };
409
409
  }
410
+ /**
411
+ * Returns Zone.js's *unpatched* native `addEventListener` / `removeEventListener`
412
+ * bound to `target`, falling back to the standard methods when Zone.js is absent.
413
+ *
414
+ * UVE reuses a single iframe and rewrites it with `document.open()/write()/close()`
415
+ * on every in-editor navigation. When Zone.js is loaded inside that iframe it runs
416
+ * in global-events mode: one native "gateway" listener per (target, eventType)
417
+ * plus a JS-level task list stored on the target node. `document.open()` tears down
418
+ * the native gateway on the persistent `window`/`document` nodes, but the task list
419
+ * survives on them, so Zone sees "already registered" on re-init and skips
420
+ * re-installing the native gateway — the listener silently goes dead after the
421
+ * first navigation.
422
+ *
423
+ * Hover/click dodge this by binding to `document.documentElement`, a node that
424
+ * `write()` recreates fresh. `scroll`/`message` (on `window`) and
425
+ * `DOMContentLoaded` (on `document`) can't: none of them fire on `<html>`, so
426
+ * there is no fresh node to rebind to. Going through Zone's native (untracked)
427
+ * methods sidesteps the dedup entirely, so the listener rebinds cleanly after
428
+ * every rewrite.
429
+ */
430
+ function getNativeEventBinder(target) {
431
+ const zone = globalThis.Zone;
432
+ const symbolFor = name => typeof zone?.__symbol__ === 'function' ? zone.__symbol__(name) : `__zone_symbol__${name}`;
433
+ const source = target;
434
+ const base = target;
435
+ const nativeAdd = source[symbolFor('addEventListener')];
436
+ const nativeRemove = source[symbolFor('removeEventListener')];
437
+ return {
438
+ addEventListener: (nativeAdd ?? base.addEventListener).bind(base),
439
+ removeEventListener: (nativeRemove ?? base.removeEventListener).bind(base)
440
+ };
441
+ }
410
442
 
411
443
  /**
412
444
  * Subscribes to content changes in the UVE editor
@@ -423,10 +455,12 @@ function onContentChanges(callback) {
423
455
  callback(event.data.payload);
424
456
  }
425
457
  };
426
- window.addEventListener('message', messageCallback);
458
+ // Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
459
+ const nativeWindow = getNativeEventBinder(window);
460
+ nativeWindow.addEventListener('message', messageCallback);
427
461
  return {
428
462
  unsubscribe: () => {
429
- window.removeEventListener('message', messageCallback);
463
+ nativeWindow.removeEventListener('message', messageCallback);
430
464
  },
431
465
  event: UVEEventType.CONTENT_CHANGES
432
466
  };
@@ -446,10 +480,12 @@ function onPageReload(callback) {
446
480
  callback();
447
481
  }
448
482
  };
449
- window.addEventListener('message', messageCallback);
483
+ // Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
484
+ const nativeWindow = getNativeEventBinder(window);
485
+ nativeWindow.addEventListener('message', messageCallback);
450
486
  return {
451
487
  unsubscribe: () => {
452
- window.removeEventListener('message', messageCallback);
488
+ nativeWindow.removeEventListener('message', messageCallback);
453
489
  },
454
490
  event: UVEEventType.PAGE_RELOAD
455
491
  };
@@ -537,13 +573,13 @@ function onAutoBounds(callback) {
537
573
  childList: true,
538
574
  subtree: true
539
575
  });
540
- // Scrolling inside the iframe doesn't change layout, so ResizeObserver
541
- // doesn't fire, but every contentlet's viewport-relative position
542
- // (getBoundingClientRect) does change. Re-emit bounds after each
543
- // scroll burst settles so the editor's pinned selected overlay
544
- // re-anchors to the on-screen position.
576
+ // Scrolling doesn't change layout (ResizeObserver won't fire) but moves
577
+ // every contentlet, so re-emit bounds after the scroll settles to re-anchor
578
+ // the selected overlay. Native binder so it survives the iframe rewrite
579
+ // under Zone.js. See getNativeEventBinder.
580
+ const nativeWindow = getNativeEventBinder(window);
545
581
  const onScroll = () => scheduleEmit();
546
- window.addEventListener('scroll', onScroll, {
582
+ nativeWindow.addEventListener('scroll', onScroll, {
547
583
  passive: true
548
584
  });
549
585
  // Flush channel: the editor occasionally needs an immediate snapshot
@@ -558,7 +594,8 @@ function onAutoBounds(callback) {
558
594
  }
559
595
  emit();
560
596
  };
561
- window.addEventListener('message', onFlush);
597
+ // Native binder (reused from `scroll` above): survives the iframe rewrite too.
598
+ nativeWindow.addEventListener('message', onFlush);
562
599
  return {
563
600
  unsubscribe: () => {
564
601
  if (debounceTimer !== null) {
@@ -567,8 +604,8 @@ function onAutoBounds(callback) {
567
604
  }
568
605
  resizeObserver.disconnect();
569
606
  mutationObserver.disconnect();
570
- window.removeEventListener('scroll', onScroll);
571
- window.removeEventListener('message', onFlush);
607
+ nativeWindow.removeEventListener('scroll', onScroll);
608
+ nativeWindow.removeEventListener('message', onFlush);
572
609
  observed = [];
573
610
  },
574
611
  event: UVEEventType.AUTO_BOUNDS
@@ -590,10 +627,12 @@ function onIframeScroll(callback) {
590
627
  callback(direction);
591
628
  }
592
629
  };
593
- window.addEventListener('message', messageCallback);
630
+ // Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
631
+ const nativeWindow = getNativeEventBinder(window);
632
+ nativeWindow.addEventListener('message', messageCallback);
594
633
  return {
595
634
  unsubscribe: () => {
596
- window.removeEventListener('message', messageCallback);
635
+ nativeWindow.removeEventListener('message', messageCallback);
597
636
  },
598
637
  event: UVEEventType.IFRAME_SCROLL
599
638
  };
@@ -623,10 +662,12 @@ function onScrollToSection(callback) {
623
662
  offsetTop: el.offsetTop
624
663
  });
625
664
  };
626
- window.addEventListener('message', messageCallback);
665
+ // Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
666
+ const nativeWindow = getNativeEventBinder(window);
667
+ nativeWindow.addEventListener('message', messageCallback);
627
668
  return {
628
669
  unsubscribe: () => {
629
- window.removeEventListener('message', messageCallback);
670
+ nativeWindow.removeEventListener('message', messageCallback);
630
671
  },
631
672
  event: UVEEventType.SCROLL_TO_SECTION
632
673
  };
@@ -696,16 +737,18 @@ function onContentletHovered(callback) {
696
737
  hasHover = true;
697
738
  callback(contentletHoveredPayload);
698
739
  };
699
- // We intentionally do not fire null on document `pointerleave`: the
700
- // editor's hover toolbar lives in the parent window (outside the
701
- // iframe), so leaving the iframe usually means the user is heading
702
- // for the toolbar. Killing the overlay there would yank the toolbar
703
- // away just as the user reaches for it. Dead-space-inside-iframe
704
- // is already covered by the `pointermove` null branch above.
705
- document.addEventListener('pointermove', pointerMoveCallback);
740
+ // We intentionally do not fire null on `pointerleave`: the editor's hover
741
+ // toolbar lives in the parent window, so leaving the iframe usually means
742
+ // the user is reaching for it killing the overlay would yank it away.
743
+ //
744
+ // Bind to `document.documentElement` (a fresh node on each iframe rewrite)
745
+ // rather than `document`, which goes dead under Zone.js after a rewrite.
746
+ // `pointermove` bubbles to <html>, so no-Zone behavior is identical. See
747
+ // getNativeEventBinder for the full rationale.
748
+ document.documentElement.addEventListener('pointermove', pointerMoveCallback);
706
749
  return {
707
750
  unsubscribe: () => {
708
- document.removeEventListener('pointermove', pointerMoveCallback);
751
+ document.documentElement.removeEventListener('pointermove', pointerMoveCallback);
709
752
  },
710
753
  event: UVEEventType.CONTENTLET_HOVERED
711
754
  };
@@ -779,18 +822,22 @@ function onContentletClicked(callback) {
779
822
  lastSelectedInode = undefined;
780
823
  }
781
824
  };
782
- // Capture phase so we run BEFORE the page's own click handlers and can
783
- // preventDefault/stopPropagation effectively.
784
- document.addEventListener('click', clickCallback, {
825
+ // Capture phase so we run BEFORE the page's own click handlers. Bound to
826
+ // `document.documentElement` (a fresh node on each iframe rewrite) rather
827
+ // than `document`, which goes dead under Zone.js after a rewrite; capture on
828
+ // <html> still runs first. See getNativeEventBinder for the full rationale.
829
+ document.documentElement.addEventListener('click', clickCallback, {
785
830
  capture: true
786
831
  });
787
- window.addEventListener('message', selectionClearedCallback);
832
+ // Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
833
+ const nativeWindow = getNativeEventBinder(window);
834
+ nativeWindow.addEventListener('message', selectionClearedCallback);
788
835
  return {
789
836
  unsubscribe: () => {
790
- document.removeEventListener('click', clickCallback, {
837
+ document.documentElement.removeEventListener('click', clickCallback, {
791
838
  capture: true
792
839
  });
793
- window.removeEventListener('message', selectionClearedCallback);
840
+ nativeWindow.removeEventListener('message', selectionClearedCallback);
794
841
  },
795
842
  event: UVEEventType.CONTENTLET_CLICKED
796
843
  };
@@ -1142,12 +1189,19 @@ function scrollHandler() {
1142
1189
  action: DotCMSUVEAction.IFRAME_SCROLL_END
1143
1190
  });
1144
1191
  };
1145
- window.addEventListener('scroll', scrollCallback);
1146
- window.addEventListener('scrollend', scrollEndCallback);
1192
+ // Native binder: scroll survives the iframe rewrite under Zone.js. It can't
1193
+ // move to a fresh `documentElement` like hover/click (viewport scroll only
1194
+ // fires on `window`). See getNativeEventBinder.
1195
+ const {
1196
+ addEventListener,
1197
+ removeEventListener
1198
+ } = getNativeEventBinder(window);
1199
+ addEventListener('scroll', scrollCallback);
1200
+ addEventListener('scrollend', scrollEndCallback);
1147
1201
  return {
1148
1202
  destroyScrollHandler: () => {
1149
- window.removeEventListener('scroll', scrollCallback);
1150
- window.removeEventListener('scrollend', scrollEndCallback);
1203
+ removeEventListener('scroll', scrollCallback);
1204
+ removeEventListener('scrollend', scrollEndCallback);
1151
1205
  }
1152
1206
  };
1153
1207
  }
@@ -1261,14 +1315,19 @@ function listenBlockEditorInlineEvent() {
1261
1315
  }
1262
1316
  };
1263
1317
  }
1264
- // If the page is not fully loaded, listen for the DOMContentLoaded event
1318
+ // Native binder: `DOMContentLoaded` fires on `document` (not `<html>`), so
1319
+ // it survives the iframe rewrite under Zone.js. See getNativeEventBinder.
1265
1320
  const handleDOMContentLoaded = () => {
1266
1321
  listenBlockEditorClick();
1267
1322
  };
1268
- document.addEventListener('DOMContentLoaded', handleDOMContentLoaded);
1323
+ const {
1324
+ addEventListener,
1325
+ removeEventListener
1326
+ } = getNativeEventBinder(document);
1327
+ addEventListener('DOMContentLoaded', handleDOMContentLoaded);
1269
1328
  return {
1270
1329
  destroyListenBlockEditorInlineEvent: () => {
1271
- document.removeEventListener('DOMContentLoaded', handleDOMContentLoaded);
1330
+ removeEventListener('DOMContentLoaded', handleDOMContentLoaded);
1272
1331
  }
1273
1332
  };
1274
1333
  }
@@ -1486,4 +1545,4 @@ function initUVE(config = {}) {
1486
1545
  };
1487
1546
  }
1488
1547
 
1489
- export { ANALYTICS_ACTIVE_WINDOW_KEY as A, getAnalyticsContentletAttributes as B, CUSTOM_NO_COMPONENT as C, DEVELOPMENT_MODE as D, END_CLASS as E, isDotAnalyticsActive as F, getContainersData as G, getContentletsInContainer as H, getDotContainerAttributes as I, readContentletDataset as J, setBounds as K, isValidBlocks as L, PRODUCTION_MODE as P, START_CLASS as S, TEMP_EMPTY_CONTENTLET as T, __UVE_EVENTS__ as _, enableBlockEditorInline as a, createContentlet as b, createUVESubscription as c, initUVE as d, editContentlet as e, __UVE_EVENT_ERROR_FALLBACK__ as f, getUVEState as g, EMPTY_CONTAINER_STYLE_REACT as h, initInlineEditing as i, EMPTY_CONTAINER_STYLE_ANGULAR as j, DOT_SECTION_ID_PREFIX as k, ANALYTICS_READY_EVENT as l, TEMP_EMPTY_CONTENTLET_TYPE as m, getDotCMSPageBounds as n, getDotCMSContentletsBound as o, getDotCMSContainerData as p, getClosestDotCMSContainerData as q, reorderMenu as r, sendMessageToUVE as s, findDotCMSElement as t, updateNavigation as u, findDotCMSVTLData as v, computeScrollIsInBottom as w, combineClasses as x, getColumnPositionClasses as y, getDotContentletAttributes as z };
1548
+ export { ANALYTICS_ACTIVE_WINDOW_KEY as A, getAnalyticsContentletAttributes as B, CUSTOM_NO_COMPONENT as C, DEVELOPMENT_MODE as D, END_CLASS as E, isDotAnalyticsActive as F, getContainersData as G, getContentletsInContainer as H, getDotContainerAttributes as I, readContentletDataset as J, getNativeEventBinder as K, setBounds as L, isValidBlocks as M, PRODUCTION_MODE as P, START_CLASS as S, TEMP_EMPTY_CONTENTLET as T, __UVE_EVENTS__ as _, enableBlockEditorInline as a, createContentlet as b, createUVESubscription as c, initUVE as d, editContentlet as e, __UVE_EVENT_ERROR_FALLBACK__ as f, getUVEState as g, EMPTY_CONTAINER_STYLE_REACT as h, initInlineEditing as i, EMPTY_CONTAINER_STYLE_ANGULAR as j, DOT_SECTION_ID_PREFIX as k, ANALYTICS_READY_EVENT as l, TEMP_EMPTY_CONTENTLET_TYPE as m, getDotCMSPageBounds as n, getDotCMSContentletsBound as o, getDotCMSContainerData as p, getClosestDotCMSContainerData as q, reorderMenu as r, sendMessageToUVE as s, findDotCMSElement as t, updateNavigation as u, findDotCMSVTLData as v, computeScrollIsInBottom as w, combineClasses as x, getColumnPositionClasses as y, getDotContentletAttributes as z };
@@ -241,3 +241,24 @@ export declare function readContentletDataset(element: HTMLElement): {
241
241
  widgetTitle: string | undefined;
242
242
  onNumberOfPages: string | undefined;
243
243
  };
244
+ /**
245
+ * Returns Zone.js's *unpatched* native `addEventListener` / `removeEventListener`
246
+ * bound to `target`, falling back to the standard methods when Zone.js is absent.
247
+ *
248
+ * UVE reuses a single iframe and rewrites it with `document.open()/write()/close()`
249
+ * on every in-editor navigation. When Zone.js is loaded inside that iframe it runs
250
+ * in global-events mode: one native "gateway" listener per (target, eventType)
251
+ * plus a JS-level task list stored on the target node. `document.open()` tears down
252
+ * the native gateway on the persistent `window`/`document` nodes, but the task list
253
+ * survives on them, so Zone sees "already registered" on re-init and skips
254
+ * re-installing the native gateway — the listener silently goes dead after the
255
+ * first navigation.
256
+ *
257
+ * Hover/click dodge this by binding to `document.documentElement`, a node that
258
+ * `write()` recreates fresh. `scroll`/`message` (on `window`) and
259
+ * `DOMContentLoaded` (on `document`) can't: none of them fire on `<html>`, so
260
+ * there is no fresh node to rebind to. Going through Zone's native (untracked)
261
+ * methods sidesteps the dedup entirely, so the listener rebinds cleanly after
262
+ * every rewrite.
263
+ */
264
+ export declare function getNativeEventBinder<T extends Window | Document>(target: T): Pick<T, 'addEventListener' | 'removeEventListener'>;