@dotcms/uve 1.6.0 → 1.7.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 +24 -22
- package/index.esm.js +1 -1
- package/internal.cjs.js +8 -1
- package/internal.esm.js +1 -1
- package/package.json +1 -1
- package/public.cjs.js +434 -211
- package/public.esm.js +427 -211
- package/src/internal/constants.d.ts +23 -0
- package/src/internal/contentlet-sentinel.constants.d.ts +13 -0
- package/src/internal/events.d.ts +36 -8
- package/src/internal/index.d.ts +1 -0
- package/src/internal.d.ts +0 -1
- package/src/lib/dom/dom.utils.d.ts +60 -1
- package/src/script/utils.d.ts +0 -28
- package/src/lib/dom/document-height-observer.d.ts +0 -18
package/public.esm.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { UVEEventType, UVE_MODE, DotCMSUVEAction } from '@dotcms/types';
|
|
2
2
|
import { __DOTCMS_UVE_EVENT__ } from '@dotcms/types/internal';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Sentinel values for the placeholder contentlet used when the UVE represents
|
|
6
|
+
* an empty container (e.g. hover / selection without a real contentlet).
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
const TEMP_EMPTY_CONTENTLET = 'TEMP_EMPTY_CONTENTLET';
|
|
11
|
+
/**
|
|
12
|
+
* Placeholder `contentType` for {@link TEMP_EMPTY_CONTENTLET}.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
const TEMP_EMPTY_CONTENTLET_TYPE = 'TEMP_EMPTY_CONTENTLET_TYPE';
|
|
17
|
+
|
|
4
18
|
/**
|
|
5
19
|
* Calculates the bounding information for each page element within the given containers.
|
|
6
20
|
*
|
|
@@ -241,6 +255,38 @@ function getDotContentletAttributes(contentlet, container) {
|
|
|
241
255
|
})
|
|
242
256
|
};
|
|
243
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
*
|
|
260
|
+
* Returns the minimal set of contentlet data attributes required by DotCMS
|
|
261
|
+
* Analytics (impression & click tracking) to identify a contentlet.
|
|
262
|
+
*
|
|
263
|
+
* Used in live mode where the full editor metadata is stripped but Analytics
|
|
264
|
+
* still needs to resolve the contentlet behind an impression/click.
|
|
265
|
+
*
|
|
266
|
+
* @param {DotCMSBasicContentlet} contentlet - The contentlet to get the attributes for
|
|
267
|
+
* @returns {DotAnalyticsContentletAttributes} The Analytics-required data attributes
|
|
268
|
+
*/
|
|
269
|
+
function getAnalyticsContentletAttributes(contentlet) {
|
|
270
|
+
return {
|
|
271
|
+
'data-dot-identifier': contentlet?.identifier,
|
|
272
|
+
'data-dot-inode': contentlet?.inode,
|
|
273
|
+
'data-dot-title': contentlet?.['widgetTitle'] || contentlet?.title,
|
|
274
|
+
'data-dot-type': contentlet?.contentType,
|
|
275
|
+
'data-dot-basetype': contentlet?.baseType
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
*
|
|
280
|
+
* Checks whether DotCMS Analytics is initialized and active on the page.
|
|
281
|
+
*
|
|
282
|
+
* The SDKs use this in live mode to decide whether to keep the minimal
|
|
283
|
+
* contentlet attributes Analytics depends on.
|
|
284
|
+
*
|
|
285
|
+
* @returns {boolean} `true` when analytics is active, otherwise `false`
|
|
286
|
+
*/
|
|
287
|
+
function isDotAnalyticsActive() {
|
|
288
|
+
return typeof window !== 'undefined' && window[ANALYTICS_ACTIVE_WINDOW_KEY] === true;
|
|
289
|
+
}
|
|
244
290
|
/**
|
|
245
291
|
*
|
|
246
292
|
*
|
|
@@ -340,6 +386,59 @@ function getDotContainerAttributes({
|
|
|
340
386
|
'data-dot-uuid': uuid
|
|
341
387
|
};
|
|
342
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* Read a contentlet's dataset attributes off a DOM element and return a
|
|
391
|
+
* normalized contentlet object. Mirrors the shape consumed by the editor's
|
|
392
|
+
* SET_BOUNDS and CONTENTLET_CLICKED events. Optionally parses the
|
|
393
|
+
* `dotStyleProperties` JSON when present.
|
|
394
|
+
*/
|
|
395
|
+
function readContentletDataset(element) {
|
|
396
|
+
const dataset = element.dataset ?? {};
|
|
397
|
+
return {
|
|
398
|
+
identifier: dataset['dotIdentifier'],
|
|
399
|
+
title: dataset['dotTitle'],
|
|
400
|
+
inode: dataset['dotInode'],
|
|
401
|
+
contentType: dataset['dotType'],
|
|
402
|
+
baseType: dataset['dotBasetype'],
|
|
403
|
+
widgetTitle: dataset['dotWidgetTitle'],
|
|
404
|
+
onNumberOfPages: dataset['dotOnNumberOfPages'],
|
|
405
|
+
...(dataset['dotStyleProperties'] && {
|
|
406
|
+
dotStyleProperties: JSON.parse(dataset['dotStyleProperties'])
|
|
407
|
+
})
|
|
408
|
+
};
|
|
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
|
+
}
|
|
343
442
|
|
|
344
443
|
/**
|
|
345
444
|
* Subscribes to content changes in the UVE editor
|
|
@@ -356,10 +455,12 @@ function onContentChanges(callback) {
|
|
|
356
455
|
callback(event.data.payload);
|
|
357
456
|
}
|
|
358
457
|
};
|
|
359
|
-
|
|
458
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
459
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
460
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
360
461
|
return {
|
|
361
462
|
unsubscribe: () => {
|
|
362
|
-
|
|
463
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
363
464
|
},
|
|
364
465
|
event: UVEEventType.CONTENT_CHANGES
|
|
365
466
|
};
|
|
@@ -379,37 +480,135 @@ function onPageReload(callback) {
|
|
|
379
480
|
callback();
|
|
380
481
|
}
|
|
381
482
|
};
|
|
382
|
-
|
|
483
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
484
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
485
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
383
486
|
return {
|
|
384
487
|
unsubscribe: () => {
|
|
385
|
-
|
|
488
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
386
489
|
},
|
|
387
490
|
event: UVEEventType.PAGE_RELOAD
|
|
388
491
|
};
|
|
389
492
|
}
|
|
493
|
+
const AUTO_BOUNDS_DEBOUNCE_MS = 100;
|
|
390
494
|
/**
|
|
391
|
-
*
|
|
495
|
+
* The single bounds-sync channel. Observes the iframe document and
|
|
496
|
+
* every `[data-dot-object="container"]` with a single ResizeObserver,
|
|
497
|
+
* debounces the trailing edge by {@link AUTO_BOUNDS_DEBOUNCE_MS}ms, and
|
|
498
|
+
* emits the full `getDotCMSPageBounds(...)` payload whenever the layout
|
|
499
|
+
* settles. Also listens on `scroll` (since scrolling moves contentlets
|
|
500
|
+
* without changing layout) and on `UVE_FLUSH_BOUNDS` (the editor's
|
|
501
|
+
* "give me bounds NOW, skip the debounce" message used during drag).
|
|
502
|
+
*
|
|
503
|
+
* Re-runs `querySelectorAll` and the observer wiring whenever a
|
|
504
|
+
* MutationObserver detects child changes that touch container nodes,
|
|
505
|
+
* so containers that mount/unmount after page-load are picked up
|
|
506
|
+
* automatically.
|
|
392
507
|
*
|
|
393
|
-
* @param {UVEEventHandler} callback - Function to be called when bounds are requested
|
|
394
|
-
* @returns {Object} Object containing unsubscribe function and event type
|
|
395
|
-
* @returns {Function} .unsubscribe - Function to remove the event listener
|
|
396
|
-
* @returns {UVEEventType} .event - The event type being subscribed to
|
|
397
508
|
* @internal
|
|
398
509
|
*/
|
|
399
|
-
function
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
510
|
+
function onAutoBounds(callback) {
|
|
511
|
+
let debounceTimer = null;
|
|
512
|
+
let observed = [];
|
|
513
|
+
const emit = () => {
|
|
514
|
+
const containers = Array.from(document.querySelectorAll('[data-dot-object="container"]'));
|
|
515
|
+
callback(getDotCMSPageBounds(containers));
|
|
516
|
+
};
|
|
517
|
+
const scheduleEmit = () => {
|
|
518
|
+
if (debounceTimer !== null) {
|
|
519
|
+
clearTimeout(debounceTimer);
|
|
405
520
|
}
|
|
521
|
+
debounceTimer = setTimeout(() => {
|
|
522
|
+
debounceTimer = null;
|
|
523
|
+
emit();
|
|
524
|
+
}, AUTO_BOUNDS_DEBOUNCE_MS);
|
|
406
525
|
};
|
|
407
|
-
|
|
526
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
527
|
+
scheduleEmit();
|
|
528
|
+
});
|
|
529
|
+
const observeAll = () => {
|
|
530
|
+
// Tear down previous observations before re-wiring.
|
|
531
|
+
for (const el of observed) {
|
|
532
|
+
resizeObserver.unobserve(el);
|
|
533
|
+
}
|
|
534
|
+
observed = Array.from(document.querySelectorAll('[data-dot-object="container"]'));
|
|
535
|
+
resizeObserver.observe(document.documentElement);
|
|
536
|
+
for (const container of observed) {
|
|
537
|
+
resizeObserver.observe(container);
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
observeAll();
|
|
541
|
+
// Containers can mount/unmount after the page first paints (route
|
|
542
|
+
// changes in headless apps, lazy-loaded sections, etc.). Re-wire only
|
|
543
|
+
// when a node carrying [data-dot-object="container"] is added or
|
|
544
|
+
// removed — ignoring text/attribute churn keeps this observer cheap on
|
|
545
|
+
// busy pages.
|
|
546
|
+
const containsContainerNode = nodes => {
|
|
547
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
548
|
+
const node = nodes[i];
|
|
549
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
const el = node;
|
|
553
|
+
if (el.matches?.('[data-dot-object="container"]') || el.querySelector?.('[data-dot-object="container"]')) {
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return false;
|
|
558
|
+
};
|
|
559
|
+
const mutationObserver = new MutationObserver(mutations => {
|
|
560
|
+
for (const m of mutations) {
|
|
561
|
+
if (m.type !== 'childList') continue;
|
|
562
|
+
if (containsContainerNode(m.addedNodes) || containsContainerNode(m.removedNodes)) {
|
|
563
|
+
observeAll();
|
|
564
|
+
scheduleEmit();
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
// The SDK script can run from <head> before <body> exists. Fall back to
|
|
570
|
+
// <html> in that case — childList+subtree on the documentElement still
|
|
571
|
+
// catches container nodes that mount once <body> arrives.
|
|
572
|
+
mutationObserver.observe(document.body ?? document.documentElement, {
|
|
573
|
+
childList: true,
|
|
574
|
+
subtree: true
|
|
575
|
+
});
|
|
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);
|
|
581
|
+
const onScroll = () => scheduleEmit();
|
|
582
|
+
nativeWindow.addEventListener('scroll', onScroll, {
|
|
583
|
+
passive: true
|
|
584
|
+
});
|
|
585
|
+
// Flush channel: the editor occasionally needs an immediate snapshot
|
|
586
|
+
// of bounds (drag enter, where the dropzone has to know container
|
|
587
|
+
// rectangles before the user moves another pixel). Bypass the
|
|
588
|
+
// debounce timer and emit synchronously.
|
|
589
|
+
const onFlush = event => {
|
|
590
|
+
if (event?.data?.name !== __DOTCMS_UVE_EVENT__.UVE_FLUSH_BOUNDS) return;
|
|
591
|
+
if (debounceTimer !== null) {
|
|
592
|
+
clearTimeout(debounceTimer);
|
|
593
|
+
debounceTimer = null;
|
|
594
|
+
}
|
|
595
|
+
emit();
|
|
596
|
+
};
|
|
597
|
+
// Native binder (reused from `scroll` above): survives the iframe rewrite too.
|
|
598
|
+
nativeWindow.addEventListener('message', onFlush);
|
|
408
599
|
return {
|
|
409
600
|
unsubscribe: () => {
|
|
410
|
-
|
|
601
|
+
if (debounceTimer !== null) {
|
|
602
|
+
clearTimeout(debounceTimer);
|
|
603
|
+
debounceTimer = null;
|
|
604
|
+
}
|
|
605
|
+
resizeObserver.disconnect();
|
|
606
|
+
mutationObserver.disconnect();
|
|
607
|
+
nativeWindow.removeEventListener('scroll', onScroll);
|
|
608
|
+
nativeWindow.removeEventListener('message', onFlush);
|
|
609
|
+
observed = [];
|
|
411
610
|
},
|
|
412
|
-
event: UVEEventType.
|
|
611
|
+
event: UVEEventType.AUTO_BOUNDS
|
|
413
612
|
};
|
|
414
613
|
}
|
|
415
614
|
/**
|
|
@@ -428,10 +627,12 @@ function onIframeScroll(callback) {
|
|
|
428
627
|
callback(direction);
|
|
429
628
|
}
|
|
430
629
|
};
|
|
431
|
-
|
|
630
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
631
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
632
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
432
633
|
return {
|
|
433
634
|
unsubscribe: () => {
|
|
434
|
-
|
|
635
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
435
636
|
},
|
|
436
637
|
event: UVEEventType.IFRAME_SCROLL
|
|
437
638
|
};
|
|
@@ -461,27 +662,45 @@ function onScrollToSection(callback) {
|
|
|
461
662
|
offsetTop: el.offsetTop
|
|
462
663
|
});
|
|
463
664
|
};
|
|
464
|
-
|
|
665
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
666
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
667
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
465
668
|
return {
|
|
466
669
|
unsubscribe: () => {
|
|
467
|
-
|
|
670
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
468
671
|
},
|
|
469
672
|
event: UVEEventType.SCROLL_TO_SECTION
|
|
470
673
|
};
|
|
471
674
|
}
|
|
472
675
|
/**
|
|
473
|
-
* Subscribes to contentlet hover events in the UVE editor
|
|
676
|
+
* Subscribes to contentlet hover events in the UVE editor.
|
|
677
|
+
*
|
|
678
|
+
* The callback is invoked with a payload while the pointer is over a
|
|
679
|
+
* DotCMS element, and once with `null` when the pointer leaves the last
|
|
680
|
+
* reported element (transitions onto dead space). The editor uses the
|
|
681
|
+
* `null` signal to clear the hover overlay so it doesn't linger over
|
|
682
|
+
* areas that no longer have a contentlet under the pointer.
|
|
474
683
|
*
|
|
475
|
-
* @param {UVEEventHandler} callback - Function to be called when
|
|
684
|
+
* @param {UVEEventHandler} callback - Function to be called when hover state changes
|
|
476
685
|
* @returns {Object} Object containing unsubscribe function and event type
|
|
477
686
|
* @returns {Function} .unsubscribe - Function to remove the event listener
|
|
478
687
|
* @returns {UVEEventType} .event - The event type being subscribed to
|
|
479
688
|
* @internal
|
|
480
689
|
*/
|
|
481
690
|
function onContentletHovered(callback) {
|
|
691
|
+
let hasHover = false;
|
|
482
692
|
const pointerMoveCallback = event => {
|
|
483
693
|
const foundElement = findDotCMSElement(event.target);
|
|
484
|
-
if (!foundElement)
|
|
694
|
+
if (!foundElement) {
|
|
695
|
+
// Transitioning from a hovered contentlet to dead space — emit
|
|
696
|
+
// a single null so the editor can clear its hover overlay.
|
|
697
|
+
// Subsequent moves over dead space are no-ops.
|
|
698
|
+
if (hasHover) {
|
|
699
|
+
hasHover = false;
|
|
700
|
+
callback(null);
|
|
701
|
+
}
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
485
704
|
const {
|
|
486
705
|
x,
|
|
487
706
|
y,
|
|
@@ -490,26 +709,15 @@ function onContentletHovered(callback) {
|
|
|
490
709
|
} = foundElement.getBoundingClientRect();
|
|
491
710
|
const isContainer = foundElement.dataset?.['dotObject'] === 'container';
|
|
492
711
|
const contentletForEmptyContainer = {
|
|
493
|
-
identifier:
|
|
494
|
-
title:
|
|
495
|
-
contentType:
|
|
712
|
+
identifier: TEMP_EMPTY_CONTENTLET,
|
|
713
|
+
title: TEMP_EMPTY_CONTENTLET,
|
|
714
|
+
contentType: TEMP_EMPTY_CONTENTLET_TYPE,
|
|
496
715
|
inode: 'TEMPY_EMPTY_CONTENTLET_INODE',
|
|
497
|
-
widgetTitle:
|
|
498
|
-
baseType:
|
|
716
|
+
widgetTitle: TEMP_EMPTY_CONTENTLET,
|
|
717
|
+
baseType: TEMP_EMPTY_CONTENTLET,
|
|
499
718
|
onNumberOfPages: 1
|
|
500
719
|
};
|
|
501
|
-
const contentlet =
|
|
502
|
-
identifier: foundElement.dataset?.['dotIdentifier'],
|
|
503
|
-
title: foundElement.dataset?.['dotTitle'],
|
|
504
|
-
inode: foundElement.dataset?.['dotInode'],
|
|
505
|
-
contentType: foundElement.dataset?.['dotType'],
|
|
506
|
-
baseType: foundElement.dataset?.['dotBasetype'],
|
|
507
|
-
widgetTitle: foundElement.dataset?.['dotWidgetTitle'],
|
|
508
|
-
onNumberOfPages: foundElement.dataset?.['dotOnNumberOfPages'],
|
|
509
|
-
...(foundElement.dataset?.['dotStyleProperties'] && {
|
|
510
|
-
dotStyleProperties: JSON.parse(foundElement.dataset['dotStyleProperties'])
|
|
511
|
-
})
|
|
512
|
-
};
|
|
720
|
+
const contentlet = readContentletDataset(foundElement);
|
|
513
721
|
const vtlFiles = findDotCMSVTLData(foundElement);
|
|
514
722
|
const contentletPayload = {
|
|
515
723
|
container:
|
|
@@ -526,16 +734,114 @@ function onContentletHovered(callback) {
|
|
|
526
734
|
height,
|
|
527
735
|
payload: contentletPayload
|
|
528
736
|
};
|
|
737
|
+
hasHover = true;
|
|
529
738
|
callback(contentletHoveredPayload);
|
|
530
739
|
};
|
|
531
|
-
|
|
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);
|
|
532
749
|
return {
|
|
533
750
|
unsubscribe: () => {
|
|
534
|
-
document.removeEventListener('pointermove', pointerMoveCallback);
|
|
751
|
+
document.documentElement.removeEventListener('pointermove', pointerMoveCallback);
|
|
535
752
|
},
|
|
536
753
|
event: UVEEventType.CONTENTLET_HOVERED
|
|
537
754
|
};
|
|
538
755
|
}
|
|
756
|
+
/**
|
|
757
|
+
* Subscribes to contentlet click events in the UVE editor.
|
|
758
|
+
*
|
|
759
|
+
* The editor's hover overlay is `pointer-events: none` so wheel events pass
|
|
760
|
+
* through to the iframe. We detect the user's selection click here instead and
|
|
761
|
+
* post it back to the editor.
|
|
762
|
+
*
|
|
763
|
+
* @param {UVEEventHandler} callback - Function to be called when a contentlet is clicked
|
|
764
|
+
* @returns {Object} Object containing unsubscribe function and event type
|
|
765
|
+
* @internal
|
|
766
|
+
*/
|
|
767
|
+
function onContentletClicked(callback) {
|
|
768
|
+
// Track the last selected contentlet so a second click on the same one
|
|
769
|
+
// lets the page's native click through (links, accordions, etc.). The
|
|
770
|
+
// first click is "select"; subsequent clicks on the selected contentlet
|
|
771
|
+
// are "interact with the page".
|
|
772
|
+
let lastSelectedInode;
|
|
773
|
+
const clickCallback = event => {
|
|
774
|
+
const foundElement = findDotCMSElement(event.target);
|
|
775
|
+
if (!foundElement) return;
|
|
776
|
+
const isContainer = foundElement.dataset?.['dotObject'] === 'container';
|
|
777
|
+
// Only emit for contentlet clicks; an empty container click is a no-op
|
|
778
|
+
// for selection purposes (there's nothing to select).
|
|
779
|
+
if (isContainer) return;
|
|
780
|
+
const inode = foundElement.dataset?.['dotInode'];
|
|
781
|
+
// If the user is clicking the already-selected contentlet, let the
|
|
782
|
+
// page handle the click natively (link navigation, button handlers,
|
|
783
|
+
// form submission). The editor selection toolbar already exposes the
|
|
784
|
+
// edit/delete/etc actions; the contentlet's own UI should still work.
|
|
785
|
+
if (inode && inode === lastSelectedInode) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
// First click on this contentlet (or a different one) — select it in
|
|
789
|
+
// the editor and block the page's natural click. Capture phase +
|
|
790
|
+
// preventDefault + stopPropagation suppresses both the default action
|
|
791
|
+
// and any subscribers further down the tree.
|
|
792
|
+
event.preventDefault();
|
|
793
|
+
event.stopPropagation();
|
|
794
|
+
lastSelectedInode = inode;
|
|
795
|
+
const {
|
|
796
|
+
x,
|
|
797
|
+
y,
|
|
798
|
+
width,
|
|
799
|
+
height
|
|
800
|
+
} = foundElement.getBoundingClientRect();
|
|
801
|
+
const contentlet = readContentletDataset(foundElement);
|
|
802
|
+
const vtlFiles = findDotCMSVTLData(foundElement);
|
|
803
|
+
callback({
|
|
804
|
+
x,
|
|
805
|
+
y,
|
|
806
|
+
width,
|
|
807
|
+
height,
|
|
808
|
+
payload: {
|
|
809
|
+
container: foundElement.dataset?.['dotContainer'] ? JSON.parse(foundElement.dataset?.['dotContainer']) : getClosestDotCMSContainerData(foundElement),
|
|
810
|
+
contentlet,
|
|
811
|
+
vtlFiles
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
};
|
|
815
|
+
// The editor clears its selection on canvas resize / scroll. When that
|
|
816
|
+
// happens, our lastSelectedInode is stale: a click on what used to be the
|
|
817
|
+
// selected contentlet would be treated as a passthrough (page click) even
|
|
818
|
+
// though the editor no longer has it selected. Listen for the
|
|
819
|
+
// UVE_SELECTION_CLEARED message and reset the tracker.
|
|
820
|
+
const selectionClearedCallback = event => {
|
|
821
|
+
if (event?.data?.name === __DOTCMS_UVE_EVENT__.UVE_SELECTION_CLEARED) {
|
|
822
|
+
lastSelectedInode = undefined;
|
|
823
|
+
}
|
|
824
|
+
};
|
|
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, {
|
|
830
|
+
capture: true
|
|
831
|
+
});
|
|
832
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
833
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
834
|
+
nativeWindow.addEventListener('message', selectionClearedCallback);
|
|
835
|
+
return {
|
|
836
|
+
unsubscribe: () => {
|
|
837
|
+
document.documentElement.removeEventListener('click', clickCallback, {
|
|
838
|
+
capture: true
|
|
839
|
+
});
|
|
840
|
+
nativeWindow.removeEventListener('message', selectionClearedCallback);
|
|
841
|
+
},
|
|
842
|
+
event: UVEEventType.CONTENTLET_CLICKED
|
|
843
|
+
};
|
|
844
|
+
}
|
|
539
845
|
|
|
540
846
|
/**
|
|
541
847
|
* Events that can be subscribed to in the UVE
|
|
@@ -550,17 +856,31 @@ const __UVE_EVENTS__ = {
|
|
|
550
856
|
[UVEEventType.PAGE_RELOAD]: callback => {
|
|
551
857
|
return onPageReload(callback);
|
|
552
858
|
},
|
|
553
|
-
[UVEEventType.REQUEST_BOUNDS]: callback => {
|
|
554
|
-
return onRequestBounds(callback);
|
|
555
|
-
},
|
|
556
859
|
[UVEEventType.IFRAME_SCROLL]: callback => {
|
|
557
860
|
return onIframeScroll(callback);
|
|
558
861
|
},
|
|
559
862
|
[UVEEventType.CONTENTLET_HOVERED]: callback => {
|
|
560
863
|
return onContentletHovered(callback);
|
|
561
864
|
},
|
|
865
|
+
[UVEEventType.CONTENTLET_CLICKED]: callback => {
|
|
866
|
+
return onContentletClicked(callback);
|
|
867
|
+
},
|
|
562
868
|
[UVEEventType.SCROLL_TO_SECTION]: callback => {
|
|
563
869
|
return onScrollToSection(callback);
|
|
870
|
+
},
|
|
871
|
+
// SELECTION_CLEARED is editor→SDK only. No public subscriber surface;
|
|
872
|
+
// onContentletClicked listens for the underlying postMessage internally
|
|
873
|
+
// to reset its lastSelectedInode tracker.
|
|
874
|
+
[UVEEventType.SELECTION_CLEARED]: _callback => {
|
|
875
|
+
return {
|
|
876
|
+
unsubscribe: () => {
|
|
877
|
+
/* no-op: SELECTION_CLEARED has no consumer-facing subscription */
|
|
878
|
+
},
|
|
879
|
+
event: UVEEventType.SELECTION_CLEARED
|
|
880
|
+
};
|
|
881
|
+
},
|
|
882
|
+
[UVEEventType.AUTO_BOUNDS]: callback => {
|
|
883
|
+
return onAutoBounds(callback);
|
|
564
884
|
}
|
|
565
885
|
};
|
|
566
886
|
/**
|
|
@@ -642,6 +962,29 @@ const CUSTOM_NO_COMPONENT = 'CustomNoComponent';
|
|
|
642
962
|
* @internal
|
|
643
963
|
*/
|
|
644
964
|
const DOT_SECTION_ID_PREFIX = 'dot-section-';
|
|
965
|
+
/**
|
|
966
|
+
* Window flag set by `@dotcms/analytics` when content analytics is initialized
|
|
967
|
+
* and active on the page.
|
|
968
|
+
*
|
|
969
|
+
* @important This value is intentionally duplicated from `@dotcms/analytics`
|
|
970
|
+
* (`ANALYTICS_WINDOWS_ACTIVE_KEY` in dot-analytics.constants.ts). The SDKs read
|
|
971
|
+
* it in live mode to decide whether to keep the minimal contentlet attributes
|
|
972
|
+
* Analytics depends on. Both constants MUST stay in sync.
|
|
973
|
+
*
|
|
974
|
+
* @internal
|
|
975
|
+
*/
|
|
976
|
+
const ANALYTICS_ACTIVE_WINDOW_KEY = '__dotAnalyticsActive__';
|
|
977
|
+
/**
|
|
978
|
+
* Event dispatched by `@dotcms/analytics` once analytics is ready. The SDKs
|
|
979
|
+
* listen for it so live-mode contentlets can re-render with the attributes
|
|
980
|
+
* Analytics needs, regardless of initialization order.
|
|
981
|
+
*
|
|
982
|
+
* @important Kept in sync with the `dotcms:analytics:ready` event dispatched by
|
|
983
|
+
* `@dotcms/analytics` (initializeContentAnalytics).
|
|
984
|
+
*
|
|
985
|
+
* @internal
|
|
986
|
+
*/
|
|
987
|
+
const ANALYTICS_READY_EVENT = 'dotcms:analytics:ready';
|
|
645
988
|
|
|
646
989
|
/**
|
|
647
990
|
* Gets the current state of the Universal Visual Editor (UVE).
|
|
@@ -728,97 +1071,6 @@ function createUVESubscription(eventType, callback) {
|
|
|
728
1071
|
return eventCallback(callback);
|
|
729
1072
|
}
|
|
730
1073
|
|
|
731
|
-
/**
|
|
732
|
-
* Observes rendered document height changes and notifies the caller after layout settles.
|
|
733
|
-
*
|
|
734
|
-
* Uses ResizeObserver on <html> for layout/viewport-driven changes and MutationObserver
|
|
735
|
-
* on <body> to catch DOM additions/removals that may shrink the page without a resize.
|
|
736
|
-
* Measurement reads `body.offsetHeight`, which tracks actual content height and
|
|
737
|
-
* decreases correctly after DOM removals, unaffected by CSS min-height on the html element.
|
|
738
|
-
*/
|
|
739
|
-
function observeDocumentHeight({
|
|
740
|
-
onHeightChange,
|
|
741
|
-
documentRef = document,
|
|
742
|
-
windowRef = window,
|
|
743
|
-
debounceMs = 50
|
|
744
|
-
}) {
|
|
745
|
-
const html = documentRef.documentElement;
|
|
746
|
-
const body = documentRef.body;
|
|
747
|
-
let debounceTimer = null;
|
|
748
|
-
let rafOuter = null;
|
|
749
|
-
let rafInner = null;
|
|
750
|
-
let lastHeight = null;
|
|
751
|
-
let destroyed = false;
|
|
752
|
-
const measureAndNotify = () => {
|
|
753
|
-
const height = body.offsetHeight;
|
|
754
|
-
if (!height || height === lastHeight) {
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
lastHeight = height;
|
|
758
|
-
onHeightChange(height);
|
|
759
|
-
};
|
|
760
|
-
const scheduleNotify = () => {
|
|
761
|
-
if (destroyed) {
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
if (debounceTimer !== null) {
|
|
765
|
-
clearTimeout(debounceTimer);
|
|
766
|
-
}
|
|
767
|
-
debounceTimer = setTimeout(() => {
|
|
768
|
-
debounceTimer = null;
|
|
769
|
-
if (destroyed) {
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
rafOuter = windowRef.requestAnimationFrame(() => {
|
|
773
|
-
rafOuter = null;
|
|
774
|
-
if (destroyed) {
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
rafInner = windowRef.requestAnimationFrame(() => {
|
|
778
|
-
rafInner = null;
|
|
779
|
-
if (!destroyed) {
|
|
780
|
-
measureAndNotify();
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
});
|
|
784
|
-
}, debounceMs);
|
|
785
|
-
};
|
|
786
|
-
const onLoad = () => scheduleNotify();
|
|
787
|
-
if (documentRef.readyState === 'complete' || documentRef.readyState === 'interactive') {
|
|
788
|
-
scheduleNotify();
|
|
789
|
-
} else {
|
|
790
|
-
windowRef.addEventListener('load', onLoad);
|
|
791
|
-
}
|
|
792
|
-
const resizeObserver = new ResizeObserver(() => scheduleNotify());
|
|
793
|
-
resizeObserver.observe(html);
|
|
794
|
-
const mutationObserver = new MutationObserver(() => scheduleNotify());
|
|
795
|
-
mutationObserver.observe(documentRef.body ?? html, {
|
|
796
|
-
childList: true,
|
|
797
|
-
subtree: true
|
|
798
|
-
});
|
|
799
|
-
return {
|
|
800
|
-
destroy: () => {
|
|
801
|
-
destroyed = true;
|
|
802
|
-
if (debounceTimer !== null) {
|
|
803
|
-
clearTimeout(debounceTimer);
|
|
804
|
-
debounceTimer = null;
|
|
805
|
-
}
|
|
806
|
-
if (rafOuter !== null) {
|
|
807
|
-
windowRef.cancelAnimationFrame(rafOuter);
|
|
808
|
-
rafOuter = null;
|
|
809
|
-
}
|
|
810
|
-
if (rafInner !== null) {
|
|
811
|
-
windowRef.cancelAnimationFrame(rafInner);
|
|
812
|
-
rafInner = null;
|
|
813
|
-
}
|
|
814
|
-
lastHeight = null;
|
|
815
|
-
resizeObserver.disconnect();
|
|
816
|
-
mutationObserver.disconnect();
|
|
817
|
-
windowRef.removeEventListener('load', onLoad);
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
|
|
822
1074
|
/**
|
|
823
1075
|
* Sets the bounds of the containers in the editor.
|
|
824
1076
|
* Retrieves the containers from the DOM and sends their position data to the editor.
|
|
@@ -921,7 +1173,6 @@ const isValidBlocks = blocks => {
|
|
|
921
1173
|
};
|
|
922
1174
|
};
|
|
923
1175
|
|
|
924
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
925
1176
|
/**
|
|
926
1177
|
* Sets up scroll event handlers for the window to notify the editor about scroll events.
|
|
927
1178
|
* Adds listeners for both 'scroll' and 'scrollend' events, sending appropriate messages
|
|
@@ -938,12 +1189,19 @@ function scrollHandler() {
|
|
|
938
1189
|
action: DotCMSUVEAction.IFRAME_SCROLL_END
|
|
939
1190
|
});
|
|
940
1191
|
};
|
|
941
|
-
|
|
942
|
-
|
|
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);
|
|
943
1201
|
return {
|
|
944
1202
|
destroyScrollHandler: () => {
|
|
945
|
-
|
|
946
|
-
|
|
1203
|
+
removeEventListener('scroll', scrollCallback);
|
|
1204
|
+
removeEventListener('scrollend', scrollEndCallback);
|
|
947
1205
|
}
|
|
948
1206
|
};
|
|
949
1207
|
}
|
|
@@ -983,9 +1241,6 @@ function registerUVEEvents() {
|
|
|
983
1241
|
const pageReloadSubscription = createUVESubscription(UVEEventType.PAGE_RELOAD, () => {
|
|
984
1242
|
window.location.reload();
|
|
985
1243
|
});
|
|
986
|
-
const requestBoundsSubscription = createUVESubscription(UVEEventType.REQUEST_BOUNDS, bounds => {
|
|
987
|
-
setBounds(bounds);
|
|
988
|
-
});
|
|
989
1244
|
const iframeScrollSubscription = createUVESubscription(UVEEventType.IFRAME_SCROLL, direction => {
|
|
990
1245
|
if (window.scrollY === 0 && direction === 'up' || computeScrollIsInBottom() && direction === 'down') {
|
|
991
1246
|
// If the iframe scroll is at the top or bottom, do not send anything.
|
|
@@ -1005,14 +1260,30 @@ function registerUVEEvents() {
|
|
|
1005
1260
|
payload: contentletHovered
|
|
1006
1261
|
});
|
|
1007
1262
|
});
|
|
1263
|
+
const contentletClickedSubscription = createUVESubscription(UVEEventType.CONTENTLET_CLICKED, contentletClicked => {
|
|
1264
|
+
sendMessageToUVE({
|
|
1265
|
+
action: DotCMSUVEAction.SET_SELECTED_CONTENTLET,
|
|
1266
|
+
payload: contentletClicked
|
|
1267
|
+
});
|
|
1268
|
+
});
|
|
1008
1269
|
const scrollToSectionSubscription = createUVESubscription(UVEEventType.SCROLL_TO_SECTION, payload => {
|
|
1009
1270
|
sendMessageToUVE({
|
|
1010
1271
|
action: DotCMSUVEAction.SECTION_OFFSET,
|
|
1011
1272
|
payload
|
|
1012
1273
|
});
|
|
1013
1274
|
});
|
|
1275
|
+
// The single bounds-sync channel. The SDK observes layout changes
|
|
1276
|
+
// inside the iframe (media-query reflows, image/font load shifts,
|
|
1277
|
+
// container mount/unmount, scroll, etc.) and emits SET_BOUNDS on the
|
|
1278
|
+
// trailing edge of a debounce window. The editor can also send a
|
|
1279
|
+
// UVE_FLUSH_BOUNDS message to request an immediate synchronous emit
|
|
1280
|
+
// (used during drag/drop, where the dropzone needs current bounds
|
|
1281
|
+
// before the user moves another pixel).
|
|
1282
|
+
const autoBoundsSubscription = createUVESubscription(UVEEventType.AUTO_BOUNDS, bounds => {
|
|
1283
|
+
setBounds(bounds);
|
|
1284
|
+
});
|
|
1014
1285
|
return {
|
|
1015
|
-
subscriptions: [pageReloadSubscription,
|
|
1286
|
+
subscriptions: [pageReloadSubscription, iframeScrollSubscription, contentletHoveredSubscription, contentletClickedSubscription, scrollToSectionSubscription, autoBoundsSubscription]
|
|
1016
1287
|
};
|
|
1017
1288
|
}
|
|
1018
1289
|
/**
|
|
@@ -1044,68 +1315,19 @@ function listenBlockEditorInlineEvent() {
|
|
|
1044
1315
|
}
|
|
1045
1316
|
};
|
|
1046
1317
|
}
|
|
1047
|
-
//
|
|
1318
|
+
// Native binder: `DOMContentLoaded` fires on `document` (not `<html>`), so
|
|
1319
|
+
// it survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
1048
1320
|
const handleDOMContentLoaded = () => {
|
|
1049
1321
|
listenBlockEditorClick();
|
|
1050
1322
|
};
|
|
1051
|
-
document.addEventListener('DOMContentLoaded', handleDOMContentLoaded);
|
|
1052
|
-
return {
|
|
1053
|
-
destroyListenBlockEditorInlineEvent: () => {
|
|
1054
|
-
document.removeEventListener('DOMContentLoaded', handleDOMContentLoaded);
|
|
1055
|
-
}
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
/**
|
|
1059
|
-
* Returns whether iframe height must be synchronized via postMessage.
|
|
1060
|
-
*
|
|
1061
|
-
* Same-origin parents can measure iframe content directly, so they do not need
|
|
1062
|
-
* child-driven height reporting. Cross-origin parents cannot access the iframe
|
|
1063
|
-
* DOM, so they still need the reporter fallback.
|
|
1064
|
-
*/
|
|
1065
|
-
function shouldReportIframeHeightToParent() {
|
|
1066
|
-
if (window.parent === window) {
|
|
1067
|
-
return false;
|
|
1068
|
-
}
|
|
1069
|
-
try {
|
|
1070
|
-
const parentDocument = window.parent.document;
|
|
1071
|
-
return !parentDocument;
|
|
1072
|
-
} catch {
|
|
1073
|
-
return true;
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
/**
|
|
1077
|
-
* Reports the iframe document height to the parent UVE shell via postMessage.
|
|
1078
|
-
*
|
|
1079
|
-
* Uses ResizeObserver on <html> for viewport/font/image-driven size changes, and
|
|
1080
|
-
* MutationObserver on <body> to catch DOM removals (e.g. contentlets deleted by
|
|
1081
|
-
* the editor) that shrink the page without triggering a resize event.
|
|
1082
|
-
*
|
|
1083
|
-
* Measurement reads `document.documentElement.offsetHeight` — the actual rendered
|
|
1084
|
-
* height of the <html> element after layout. `scrollHeight` is intentionally avoided
|
|
1085
|
-
* because it does not reliably decrease when content is removed from the DOM.
|
|
1086
|
-
*
|
|
1087
|
-
* Height sends are coalesced to at most one per double-requestAnimationFrame pair
|
|
1088
|
-
* so they always run after layout and paint have settled.
|
|
1089
|
-
*
|
|
1090
|
-
* @returns {{ destroyHeightReporter: () => void }} Cleanup function that removes
|
|
1091
|
-
* all listeners and disconnects the observers.
|
|
1092
|
-
*/
|
|
1093
|
-
function reportIframeHeight() {
|
|
1094
1323
|
const {
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
action: DotCMSUVEAction.IFRAME_HEIGHT,
|
|
1100
|
-
payload: {
|
|
1101
|
-
height
|
|
1102
|
-
}
|
|
1103
|
-
});
|
|
1104
|
-
}
|
|
1105
|
-
});
|
|
1324
|
+
addEventListener,
|
|
1325
|
+
removeEventListener
|
|
1326
|
+
} = getNativeEventBinder(document);
|
|
1327
|
+
addEventListener('DOMContentLoaded', handleDOMContentLoaded);
|
|
1106
1328
|
return {
|
|
1107
|
-
|
|
1108
|
-
|
|
1329
|
+
destroyListenBlockEditorInlineEvent: () => {
|
|
1330
|
+
removeEventListener('DOMContentLoaded', handleDOMContentLoaded);
|
|
1109
1331
|
}
|
|
1110
1332
|
};
|
|
1111
1333
|
}
|
|
@@ -1314,19 +1536,13 @@ function initUVE(config = {}) {
|
|
|
1314
1536
|
const {
|
|
1315
1537
|
destroyListenBlockEditorInlineEvent
|
|
1316
1538
|
} = listenBlockEditorInlineEvent();
|
|
1317
|
-
const {
|
|
1318
|
-
destroyHeightReporter
|
|
1319
|
-
} = shouldReportIframeHeightToParent() ? reportIframeHeight() : {
|
|
1320
|
-
destroyHeightReporter: () => undefined
|
|
1321
|
-
};
|
|
1322
1539
|
return {
|
|
1323
1540
|
destroyUVESubscriptions: () => {
|
|
1324
1541
|
subscriptions.forEach(subscription => subscription.unsubscribe());
|
|
1325
1542
|
destroyScrollHandler();
|
|
1326
1543
|
destroyListenBlockEditorInlineEvent();
|
|
1327
|
-
destroyHeightReporter();
|
|
1328
1544
|
}
|
|
1329
1545
|
};
|
|
1330
1546
|
}
|
|
1331
1547
|
|
|
1332
|
-
export {
|
|
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 };
|