@gemx-dev/heatmap-react 3.5.92-dev.1 → 3.5.92-dev.11
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/dist/esm/components/Layout/HeatmapLayout.d.ts +1 -0
- package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -1
- package/dist/esm/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
- package/dist/esm/hooks/common/useHeatmapWidthByDevice.d.ts.map +1 -1
- package/dist/esm/hooks/register/useRegisterConfig.d.ts +2 -1
- package/dist/esm/hooks/register/useRegisterConfig.d.ts.map +1 -1
- package/dist/esm/hooks/view-context/index.d.ts +1 -0
- package/dist/esm/hooks/view-context/index.d.ts.map +1 -1
- package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -1
- package/dist/esm/hooks/view-context/useHeatmapDataContext.d.ts +4 -16
- package/dist/esm/hooks/view-context/useHeatmapDataContext.d.ts.map +1 -1
- package/dist/esm/hooks/view-context/useHeatmapLiveContext.d.ts +34 -0
- package/dist/esm/hooks/view-context/useHeatmapLiveContext.d.ts.map +1 -0
- package/dist/esm/hooks/view-context/useHeatmapSettingContext.d.ts +3 -1
- package/dist/esm/hooks/view-context/useHeatmapSettingContext.d.ts.map +1 -1
- package/dist/esm/hooks/view-context/useHeatmapVizContext.d.ts +2 -2
- package/dist/esm/hooks/view-context/useHeatmapVizContext.d.ts.map +1 -1
- package/dist/esm/hooks/viz-live/useVizLiveIframeMsg.d.ts.map +1 -1
- package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
- package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1221 -616
- package/dist/esm/index.mjs +1221 -616
- package/dist/esm/libs/iframe-processor/index.d.ts +2 -5
- package/dist/esm/libs/iframe-processor/index.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/lifecycle.d.ts +15 -7
- package/dist/esm/libs/iframe-processor/lifecycle.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/orchestrator.d.ts +13 -45
- package/dist/esm/libs/iframe-processor/orchestrator.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/height-observer/index.d.ts +3 -14
- package/dist/esm/libs/iframe-processor/processors/height-observer/index.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/height-observer/types.d.ts +9 -1
- package/dist/esm/libs/iframe-processor/processors/height-observer/types.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/navigation/index.d.ts +3 -14
- package/dist/esm/libs/iframe-processor/processors/navigation/index.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/navigation/listeners.d.ts +3 -4
- package/dist/esm/libs/iframe-processor/processors/navigation/listeners.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/navigation/types.d.ts +13 -0
- package/dist/esm/libs/iframe-processor/processors/navigation/types.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/global-fixes/viewport-unit-replacer/fixes.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/gp-v7-fixes/gem-slider-item/fixes.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/types.d.ts +2 -2
- package/dist/esm/libs/iframe-processor/processors/viewport/index.d.ts +3 -9
- package/dist/esm/libs/iframe-processor/processors/viewport/index.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/viewport/listeners.d.ts +3 -2
- package/dist/esm/libs/iframe-processor/processors/viewport/listeners.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/viewport/pipeline.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts +2 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/constants.d.ts +5 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/constants.d.ts.map +1 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/functions.d.ts +11 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/functions.d.ts.map +1 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/index.d.ts +9 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/index.d.ts.map +1 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/state.d.ts +13 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/state.d.ts.map +1 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/types.d.ts +16 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/types.d.ts.map +1 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/types.d.ts +8 -0
- package/dist/esm/libs/iframe-processor/processors/viewport/types.d.ts.map +1 -0
- package/dist/esm/libs/iframe-processor/shared/factory-types.d.ts +24 -0
- package/dist/esm/libs/iframe-processor/shared/factory-types.d.ts.map +1 -0
- package/dist/esm/libs/iframe-processor/shared/iframe-types.d.ts +20 -0
- package/dist/esm/libs/iframe-processor/shared/iframe-types.d.ts.map +1 -0
- package/dist/esm/libs/index.d.ts +1 -0
- package/dist/esm/libs/index.d.ts.map +1 -1
- package/dist/esm/libs/perf.d.ts +83 -0
- package/dist/esm/libs/perf.d.ts.map +1 -0
- package/dist/esm/stores/config.d.ts +2 -2
- package/dist/esm/stores/config.d.ts.map +1 -1
- package/dist/esm/stores/data.d.ts +4 -0
- package/dist/esm/stores/data.d.ts.map +1 -1
- package/dist/esm/stores/mode-live.d.ts +30 -16
- package/dist/esm/stores/mode-live.d.ts.map +1 -1
- package/dist/esm/stores/setting.d.ts +3 -1
- package/dist/esm/stores/setting.d.ts.map +1 -1
- package/dist/esm/stores/viz.d.ts +2 -2
- package/dist/esm/stores/viz.d.ts.map +1 -1
- package/dist/esm/types/heatmap.d.ts +5 -0
- package/dist/esm/types/heatmap.d.ts.map +1 -1
- package/dist/esm/types/iframe-helper.d.ts +0 -19
- package/dist/esm/types/iframe-helper.d.ts.map +1 -1
- package/dist/umd/components/Layout/HeatmapLayout.d.ts +1 -0
- package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
- package/dist/umd/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
- package/dist/umd/hooks/common/useHeatmapWidthByDevice.d.ts.map +1 -1
- package/dist/umd/hooks/register/useRegisterConfig.d.ts +2 -1
- package/dist/umd/hooks/register/useRegisterConfig.d.ts.map +1 -1
- package/dist/umd/hooks/view-context/index.d.ts +1 -0
- package/dist/umd/hooks/view-context/index.d.ts.map +1 -1
- package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -1
- package/dist/umd/hooks/view-context/useHeatmapDataContext.d.ts +4 -16
- package/dist/umd/hooks/view-context/useHeatmapDataContext.d.ts.map +1 -1
- package/dist/umd/hooks/view-context/useHeatmapLiveContext.d.ts +34 -0
- package/dist/umd/hooks/view-context/useHeatmapLiveContext.d.ts.map +1 -0
- package/dist/umd/hooks/view-context/useHeatmapSettingContext.d.ts +3 -1
- package/dist/umd/hooks/view-context/useHeatmapSettingContext.d.ts.map +1 -1
- package/dist/umd/hooks/view-context/useHeatmapVizContext.d.ts +2 -2
- package/dist/umd/hooks/view-context/useHeatmapVizContext.d.ts.map +1 -1
- package/dist/umd/hooks/viz-live/useVizLiveIframeMsg.d.ts.map +1 -1
- package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
- package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
- package/dist/umd/index.d.ts +1 -1
- package/dist/umd/index.d.ts.map +1 -1
- package/dist/umd/index.js +2 -2
- package/dist/umd/libs/iframe-processor/index.d.ts +2 -5
- package/dist/umd/libs/iframe-processor/index.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/lifecycle.d.ts +15 -7
- package/dist/umd/libs/iframe-processor/lifecycle.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/orchestrator.d.ts +13 -45
- package/dist/umd/libs/iframe-processor/orchestrator.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/height-observer/index.d.ts +3 -14
- package/dist/umd/libs/iframe-processor/processors/height-observer/index.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/height-observer/types.d.ts +9 -1
- package/dist/umd/libs/iframe-processor/processors/height-observer/types.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/navigation/index.d.ts +3 -14
- package/dist/umd/libs/iframe-processor/processors/navigation/index.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/navigation/listeners.d.ts +3 -4
- package/dist/umd/libs/iframe-processor/processors/navigation/listeners.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/navigation/types.d.ts +13 -0
- package/dist/umd/libs/iframe-processor/processors/navigation/types.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/global-fixes/viewport-unit-replacer/fixes.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/gp-v7-fixes/gem-slider-item/fixes.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/types.d.ts +2 -2
- package/dist/umd/libs/iframe-processor/processors/viewport/index.d.ts +3 -9
- package/dist/umd/libs/iframe-processor/processors/viewport/index.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/viewport/listeners.d.ts +3 -2
- package/dist/umd/libs/iframe-processor/processors/viewport/listeners.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/viewport/pipeline.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts +2 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/constants.d.ts +5 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/constants.d.ts.map +1 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/functions.d.ts +11 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/functions.d.ts.map +1 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/index.d.ts +9 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/index.d.ts.map +1 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/state.d.ts +13 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/state.d.ts.map +1 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/types.d.ts +16 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/types.d.ts.map +1 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/types.d.ts +8 -0
- package/dist/umd/libs/iframe-processor/processors/viewport/types.d.ts.map +1 -0
- package/dist/umd/libs/iframe-processor/shared/factory-types.d.ts +24 -0
- package/dist/umd/libs/iframe-processor/shared/factory-types.d.ts.map +1 -0
- package/dist/umd/libs/iframe-processor/shared/iframe-types.d.ts +20 -0
- package/dist/umd/libs/iframe-processor/shared/iframe-types.d.ts.map +1 -0
- package/dist/umd/libs/index.d.ts +1 -0
- package/dist/umd/libs/index.d.ts.map +1 -1
- package/dist/umd/libs/perf.d.ts +83 -0
- package/dist/umd/libs/perf.d.ts.map +1 -0
- package/dist/umd/stores/config.d.ts +2 -2
- package/dist/umd/stores/config.d.ts.map +1 -1
- package/dist/umd/stores/data.d.ts +4 -0
- package/dist/umd/stores/data.d.ts.map +1 -1
- package/dist/umd/stores/mode-live.d.ts +30 -16
- package/dist/umd/stores/mode-live.d.ts.map +1 -1
- package/dist/umd/stores/setting.d.ts +3 -1
- package/dist/umd/stores/setting.d.ts.map +1 -1
- package/dist/umd/stores/viz.d.ts +2 -2
- package/dist/umd/stores/viz.d.ts.map +1 -1
- package/dist/umd/types/heatmap.d.ts +5 -0
- package/dist/umd/types/heatmap.d.ts.map +1 -1
- package/dist/umd/types/iframe-helper.d.ts +0 -19
- package/dist/umd/types/iframe-helper.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer.d.ts +0 -54
- package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer.d.ts.map +0 -1
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer.d.ts +0 -54
- package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer.d.ts.map +0 -1
package/dist/esm/index.mjs
CHANGED
|
@@ -152,6 +152,7 @@ function useDebounceCallback(callback, delay) {
|
|
|
152
152
|
|
|
153
153
|
var EDeviceType;
|
|
154
154
|
(function (EDeviceType) {
|
|
155
|
+
EDeviceType["DesktopLarge"] = "DESKTOP_LARGE";
|
|
155
156
|
EDeviceType["Desktop"] = "DESKTOP";
|
|
156
157
|
EDeviceType["Mobile"] = "MOBILE";
|
|
157
158
|
EDeviceType["Tablet"] = "TABLET";
|
|
@@ -203,6 +204,11 @@ var EHeatmapMode;
|
|
|
203
204
|
EHeatmapMode["Live"] = "live";
|
|
204
205
|
EHeatmapMode["Compare"] = "compare";
|
|
205
206
|
})(EHeatmapMode || (EHeatmapMode = {}));
|
|
207
|
+
var EHeatmapDataSource;
|
|
208
|
+
(function (EHeatmapDataSource) {
|
|
209
|
+
EHeatmapDataSource["Snapshot"] = "snapshot";
|
|
210
|
+
EHeatmapDataSource["Live"] = "live";
|
|
211
|
+
})(EHeatmapDataSource || (EHeatmapDataSource = {}));
|
|
206
212
|
|
|
207
213
|
const ViewIdContext = createContext(undefined);
|
|
208
214
|
const useViewIdContext = () => {
|
|
@@ -314,18 +320,19 @@ const useHeatmapConfigStore = create()((set) => {
|
|
|
314
320
|
return {
|
|
315
321
|
mode: EHeatmapMode.Single,
|
|
316
322
|
sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
|
|
317
|
-
|
|
318
|
-
isRendering: true,
|
|
323
|
+
shopId: undefined,
|
|
319
324
|
setMode: (mode) => set({ mode }),
|
|
320
325
|
resetMode: () => set({ mode: EHeatmapMode.Single }),
|
|
321
326
|
setSidebarWidth: (sidebarWidth) => set({ sidebarWidth }),
|
|
322
|
-
|
|
327
|
+
setShopId: (shopId) => set({ shopId }),
|
|
323
328
|
};
|
|
324
329
|
});
|
|
325
330
|
|
|
326
331
|
const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
327
332
|
return {
|
|
328
333
|
data: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
334
|
+
dataHash: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
335
|
+
dataSnapshot: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
329
336
|
clickmap: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
330
337
|
clickAreas: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
331
338
|
dataInfo: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
@@ -358,6 +365,16 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
358
365
|
newData.set(viewId, data);
|
|
359
366
|
return { data: newData };
|
|
360
367
|
}),
|
|
368
|
+
setDataHash: (dataHash, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
369
|
+
const newDataHash = new Map(prev.dataHash);
|
|
370
|
+
newDataHash.set(viewId, dataHash);
|
|
371
|
+
return { dataHash: newDataHash };
|
|
372
|
+
}),
|
|
373
|
+
setDataSnapshot: (data, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
374
|
+
const newDataSnapshot = new Map(prev.dataSnapshot);
|
|
375
|
+
newDataSnapshot.set(viewId, data);
|
|
376
|
+
return { dataSnapshot: newDataSnapshot };
|
|
377
|
+
}),
|
|
361
378
|
setClickmap: (clickmap, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
362
379
|
const newClickmap = new Map(prev.clickmap);
|
|
363
380
|
newClickmap.set(viewId, clickmap);
|
|
@@ -380,12 +397,16 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
380
397
|
}),
|
|
381
398
|
copyView: (fromViewId, toViewId) => set((prev) => {
|
|
382
399
|
const newData = new Map(prev.data);
|
|
400
|
+
const newDataSnapshot = new Map(prev.dataSnapshot);
|
|
383
401
|
const newClickmap = new Map(prev.clickmap);
|
|
384
402
|
const newClickAreas = new Map(prev.clickAreas);
|
|
385
403
|
const newDataInfo = new Map(prev.dataInfo);
|
|
386
404
|
const newScrollmap = new Map(prev.scrollmap);
|
|
387
405
|
const newAttentionMap = new Map(prev.attentionMap);
|
|
406
|
+
const newDataHash = new Map(prev.dataHash);
|
|
388
407
|
newData.set(toViewId, prev.data.get(fromViewId));
|
|
408
|
+
newDataSnapshot.set(toViewId, prev.dataSnapshot.get(fromViewId));
|
|
409
|
+
newDataHash.set(toViewId, prev.dataHash.get(fromViewId));
|
|
389
410
|
newClickmap.set(toViewId, prev.clickmap.get(fromViewId));
|
|
390
411
|
newClickAreas.set(toViewId, prev.clickAreas.get(fromViewId));
|
|
391
412
|
newDataInfo.set(toViewId, prev.dataInfo.get(fromViewId));
|
|
@@ -393,21 +414,27 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
393
414
|
newAttentionMap.set(toViewId, prev.attentionMap.get(fromViewId));
|
|
394
415
|
return {
|
|
395
416
|
data: newData,
|
|
417
|
+
dataSnapshot: newDataSnapshot,
|
|
396
418
|
clickmap: newClickmap,
|
|
397
419
|
clickAreas: newClickAreas,
|
|
398
420
|
dataInfo: newDataInfo,
|
|
399
421
|
scrollmap: newScrollmap,
|
|
400
422
|
attentionMap: newAttentionMap,
|
|
423
|
+
dataHash: newDataHash,
|
|
401
424
|
};
|
|
402
425
|
}),
|
|
403
426
|
clearView: (viewId) => set((prev) => {
|
|
404
427
|
const newData = new Map(prev.data);
|
|
428
|
+
const newDataSnapshot = new Map(prev.dataSnapshot);
|
|
405
429
|
const newClickmap = new Map(prev.clickmap);
|
|
406
430
|
const newClickAreas = new Map(prev.clickAreas);
|
|
407
431
|
const newDataInfo = new Map(prev.dataInfo);
|
|
408
432
|
const newScrollmap = new Map(prev.scrollmap);
|
|
409
433
|
const newAttentionMap = new Map(prev.attentionMap);
|
|
434
|
+
const newDataHash = new Map(prev.dataHash);
|
|
410
435
|
newData.delete(viewId);
|
|
436
|
+
newDataSnapshot.delete(viewId);
|
|
437
|
+
newDataHash.delete(viewId);
|
|
411
438
|
newClickmap.delete(viewId);
|
|
412
439
|
newClickAreas.delete(viewId);
|
|
413
440
|
newDataInfo.delete(viewId);
|
|
@@ -415,6 +442,8 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
415
442
|
newAttentionMap.delete(viewId);
|
|
416
443
|
return {
|
|
417
444
|
data: newData,
|
|
445
|
+
dataSnapshot: newDataSnapshot,
|
|
446
|
+
dataHash: newDataHash,
|
|
418
447
|
clickmap: newClickmap,
|
|
419
448
|
clickAreas: newClickAreas,
|
|
420
449
|
dataInfo: newDataInfo,
|
|
@@ -424,6 +453,8 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
424
453
|
}),
|
|
425
454
|
resetAll: () => set({
|
|
426
455
|
data: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
456
|
+
dataSnapshot: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
457
|
+
dataHash: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
427
458
|
clickmap: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
428
459
|
clickAreas: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
429
460
|
dataInfo: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
@@ -445,6 +476,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
445
476
|
clickMode: new Map([[DEFAULT_VIEW_ID, EClickMode.Default]]),
|
|
446
477
|
scrollType: new Map([[DEFAULT_VIEW_ID, EScrollType.Depth]]),
|
|
447
478
|
heatmapType: new Map([[DEFAULT_VIEW_ID, EHeatmapType.Click]]),
|
|
479
|
+
dataSource: new Map([[DEFAULT_VIEW_ID, EHeatmapDataSource.Snapshot]]),
|
|
448
480
|
setIsRendering: (isRendering, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
449
481
|
const newIsRendering = new Map(prev.isRendering);
|
|
450
482
|
newIsRendering.set(viewId, isRendering);
|
|
@@ -495,6 +527,11 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
495
527
|
newHeatmapType.set(viewId, heatmapType);
|
|
496
528
|
return { heatmapType: newHeatmapType };
|
|
497
529
|
}),
|
|
530
|
+
setDataSource: (dataSource, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
531
|
+
const newDataSource = new Map(prev.dataSource);
|
|
532
|
+
newDataSource.set(viewId, dataSource);
|
|
533
|
+
return { dataSource: newDataSource };
|
|
534
|
+
}),
|
|
498
535
|
copyView: (fromViewId, toViewId) => set((prev) => {
|
|
499
536
|
const newIsLoadingDom = new Map(prev.isLoadingDom);
|
|
500
537
|
const newIsLoadingCanvas = new Map(prev.isLoadingCanvas);
|
|
@@ -505,6 +542,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
505
542
|
const newClickMode = new Map(prev.clickMode);
|
|
506
543
|
const newScrollType = new Map(prev.scrollType);
|
|
507
544
|
const newHeatmapType = new Map(prev.heatmapType);
|
|
545
|
+
const newDataSource = new Map(prev.dataSource);
|
|
508
546
|
newIsShowSidebar.set(toViewId, prev.isShowSidebar.get(fromViewId) ?? false);
|
|
509
547
|
newRankedBy.set(toViewId, prev.rankedBy.get(fromViewId));
|
|
510
548
|
newDeviceType.set(toViewId, prev.deviceType.get(fromViewId));
|
|
@@ -514,6 +552,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
514
552
|
newHeatmapType.set(toViewId, prev.heatmapType.get(fromViewId));
|
|
515
553
|
newIsLoadingDom.set(toViewId, prev.isLoadingDom.get(fromViewId) ?? false);
|
|
516
554
|
newIsLoadingCanvas.set(toViewId, prev.isLoadingCanvas.get(fromViewId) ?? false);
|
|
555
|
+
newDataSource.set(toViewId, prev.dataSource.get(fromViewId) ?? EHeatmapDataSource.Snapshot);
|
|
517
556
|
return {
|
|
518
557
|
isShowSidebar: newIsShowSidebar,
|
|
519
558
|
rankedBy: newRankedBy,
|
|
@@ -524,6 +563,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
524
563
|
clickMode: newClickMode,
|
|
525
564
|
scrollType: newScrollType,
|
|
526
565
|
heatmapType: newHeatmapType,
|
|
566
|
+
dataSource: newDataSource,
|
|
527
567
|
};
|
|
528
568
|
}),
|
|
529
569
|
clearView: (viewId) => set((prev) => {
|
|
@@ -536,6 +576,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
536
576
|
const newClickMode = new Map(prev.clickMode);
|
|
537
577
|
const newScrollType = new Map(prev.scrollType);
|
|
538
578
|
const newHeatmapType = new Map(prev.heatmapType);
|
|
579
|
+
const newDataSource = new Map(prev.dataSource);
|
|
539
580
|
newIsShowSidebar.delete(viewId);
|
|
540
581
|
newRankedBy.delete(viewId);
|
|
541
582
|
newIsLoadingDom.delete(viewId);
|
|
@@ -545,6 +586,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
545
586
|
newClickMode.delete(viewId);
|
|
546
587
|
newScrollType.delete(viewId);
|
|
547
588
|
newHeatmapType.delete(viewId);
|
|
589
|
+
newDataSource.delete(viewId);
|
|
548
590
|
return {
|
|
549
591
|
isShowSidebar: newIsShowSidebar,
|
|
550
592
|
rankedBy: newRankedBy,
|
|
@@ -555,6 +597,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
555
597
|
clickMode: newClickMode,
|
|
556
598
|
scrollType: newScrollType,
|
|
557
599
|
heatmapType: newHeatmapType,
|
|
600
|
+
dataSource: newDataSource,
|
|
558
601
|
};
|
|
559
602
|
}),
|
|
560
603
|
resetAll: () => set({
|
|
@@ -568,22 +611,23 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
568
611
|
clickMode: new Map([[DEFAULT_VIEW_ID, EClickMode.Default]]),
|
|
569
612
|
scrollType: new Map([[DEFAULT_VIEW_ID, EScrollType.Depth]]),
|
|
570
613
|
heatmapType: new Map([[DEFAULT_VIEW_ID, EHeatmapType.Click]]),
|
|
614
|
+
dataSource: new Map([[DEFAULT_VIEW_ID, EHeatmapDataSource.Snapshot]]),
|
|
571
615
|
}),
|
|
572
616
|
};
|
|
573
617
|
}));
|
|
574
618
|
|
|
575
619
|
const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
|
|
576
620
|
return {
|
|
577
|
-
|
|
621
|
+
isRenderedViz: new Map([[DEFAULT_VIEW_ID, false]]),
|
|
578
622
|
zoomRatio: new Map([[DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO.DEFAULT]]),
|
|
579
623
|
minZoomRatio: new Map([[DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO.MIN]]),
|
|
580
624
|
maxZoomRatio: new Map([[DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO.MAX]]),
|
|
581
625
|
scale: new Map([[DEFAULT_VIEW_ID, 1]]),
|
|
582
626
|
isScaledToFit: new Map([[DEFAULT_VIEW_ID, false]]),
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
return {
|
|
627
|
+
setIsRenderedViz: (isRenderedViz, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
628
|
+
const newIsRenderedViz = new Map(prev.isRenderedViz);
|
|
629
|
+
newIsRenderedViz.set(viewId, isRenderedViz);
|
|
630
|
+
return { isRenderedViz: newIsRenderedViz };
|
|
587
631
|
}),
|
|
588
632
|
setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
589
633
|
const newZoomRatio = new Map(prev.zoomRatio);
|
|
@@ -611,18 +655,18 @@ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
|
|
|
611
655
|
return { isScaledToFit: newIsScaledToFit };
|
|
612
656
|
}),
|
|
613
657
|
copyView: (fromViewId, toViewId) => set((prev) => {
|
|
614
|
-
const
|
|
658
|
+
const newIsRenderedViz = new Map(prev.isRenderedViz);
|
|
615
659
|
const newZoomRatio = new Map(prev.zoomRatio);
|
|
616
660
|
const newMinZoomRatio = new Map(prev.minZoomRatio);
|
|
617
661
|
const newScale = new Map(prev.scale);
|
|
618
662
|
const newIsScaledToFit = new Map(prev.isScaledToFit);
|
|
619
|
-
|
|
663
|
+
newIsRenderedViz.set(toViewId, prev.isRenderedViz.get(fromViewId) ?? false);
|
|
620
664
|
newZoomRatio.set(toViewId, prev.zoomRatio.get(fromViewId) ?? 100);
|
|
621
665
|
newMinZoomRatio.set(toViewId, prev.minZoomRatio.get(fromViewId) ?? 10);
|
|
622
666
|
newScale.set(toViewId, prev.scale.get(fromViewId) ?? 1);
|
|
623
667
|
newIsScaledToFit.set(toViewId, prev.isScaledToFit.get(fromViewId) ?? false);
|
|
624
668
|
return {
|
|
625
|
-
|
|
669
|
+
isRenderedViz: newIsRenderedViz,
|
|
626
670
|
zoomRatio: newZoomRatio,
|
|
627
671
|
minZoomRatio: newMinZoomRatio,
|
|
628
672
|
scale: newScale,
|
|
@@ -630,18 +674,18 @@ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
|
|
|
630
674
|
};
|
|
631
675
|
}),
|
|
632
676
|
clearView: (viewId) => set((prev) => {
|
|
633
|
-
const
|
|
677
|
+
const newIsRenderedViz = new Map(prev.isRenderedViz);
|
|
634
678
|
const newZoomRatio = new Map(prev.zoomRatio);
|
|
635
679
|
const newMinZoomRatio = new Map(prev.minZoomRatio);
|
|
636
680
|
const newScale = new Map(prev.scale);
|
|
637
681
|
const newIsScaledToFit = new Map(prev.isScaledToFit);
|
|
638
|
-
|
|
682
|
+
newIsRenderedViz.delete(viewId);
|
|
639
683
|
newZoomRatio.delete(viewId);
|
|
640
684
|
newMinZoomRatio.delete(viewId);
|
|
641
685
|
newScale.delete(viewId);
|
|
642
686
|
newIsScaledToFit.delete(viewId);
|
|
643
687
|
return {
|
|
644
|
-
|
|
688
|
+
isRenderedViz: newIsRenderedViz,
|
|
645
689
|
zoomRatio: newZoomRatio,
|
|
646
690
|
minZoomRatio: newMinZoomRatio,
|
|
647
691
|
scale: newScale,
|
|
@@ -649,7 +693,7 @@ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
|
|
|
649
693
|
};
|
|
650
694
|
}),
|
|
651
695
|
resetAll: () => set({
|
|
652
|
-
|
|
696
|
+
isRenderedViz: new Map([[DEFAULT_VIEW_ID, false]]),
|
|
653
697
|
zoomRatio: new Map([[DEFAULT_VIEW_ID, 100]]),
|
|
654
698
|
minZoomRatio: new Map([[DEFAULT_VIEW_ID, 10]]),
|
|
655
699
|
scale: new Map([[DEFAULT_VIEW_ID, 1]]),
|
|
@@ -1047,24 +1091,108 @@ const useHeatmapCompareStore = create()((set, get) => {
|
|
|
1047
1091
|
});
|
|
1048
1092
|
|
|
1049
1093
|
const initialState = {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1094
|
+
decodedPayloads: new Map([[DEFAULT_VIEW_ID, []]]),
|
|
1095
|
+
encodedPayloads: new Map([[DEFAULT_VIEW_ID, []]]),
|
|
1096
|
+
htmlContent: new Map([[DEFAULT_VIEW_ID, '']]),
|
|
1097
|
+
targetUrl: new Map([[DEFAULT_VIEW_ID, '']]),
|
|
1098
|
+
renderMode: new Map([[DEFAULT_VIEW_ID, 'portal']]),
|
|
1099
|
+
storefrontPassword: new Map([[DEFAULT_VIEW_ID, '']]),
|
|
1100
|
+
};
|
|
1101
|
+
const useHeatmapLiveStore = create()(subscribeWithSelector((set) => ({
|
|
1102
|
+
...initialState,
|
|
1103
|
+
addPayload: (payload, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1104
|
+
const newDecoded = new Map(prev.decodedPayloads);
|
|
1105
|
+
newDecoded.set(viewId, [...(newDecoded.get(viewId) ?? []), payload]);
|
|
1106
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1107
|
+
newEncoded.set(viewId, [...(newEncoded.get(viewId) ?? []), JSON.stringify(payload)]);
|
|
1108
|
+
return { decodedPayloads: newDecoded, encodedPayloads: newEncoded };
|
|
1109
|
+
}),
|
|
1110
|
+
setPayloads: (payloads, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1111
|
+
const newDecoded = new Map(prev.decodedPayloads);
|
|
1112
|
+
newDecoded.set(viewId, payloads);
|
|
1113
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1114
|
+
newEncoded.set(viewId, payloads.map((p) => JSON.stringify(p)));
|
|
1115
|
+
return { decodedPayloads: newDecoded, encodedPayloads: newEncoded };
|
|
1116
|
+
}),
|
|
1117
|
+
setEncodedPayloads: (payloads, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1118
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1119
|
+
newEncoded.set(viewId, payloads);
|
|
1120
|
+
return { encodedPayloads: newEncoded };
|
|
1121
|
+
}),
|
|
1122
|
+
setHtmlContent: (htmlContent, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1123
|
+
const newHtmlContent = new Map(prev.htmlContent);
|
|
1124
|
+
newHtmlContent.set(viewId, htmlContent);
|
|
1125
|
+
return { htmlContent: newHtmlContent };
|
|
1126
|
+
}),
|
|
1127
|
+
setTargetUrl: (targetUrl, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1128
|
+
const newTargetUrl = new Map(prev.targetUrl);
|
|
1129
|
+
newTargetUrl.set(viewId, targetUrl);
|
|
1130
|
+
return { targetUrl: newTargetUrl };
|
|
1131
|
+
}),
|
|
1132
|
+
setRenderMode: (renderMode, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1133
|
+
const newRenderMode = new Map(prev.renderMode);
|
|
1134
|
+
newRenderMode.set(viewId, renderMode);
|
|
1135
|
+
return { renderMode: newRenderMode };
|
|
1136
|
+
}),
|
|
1137
|
+
setStorefrontPassword: (storefrontPassword, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1138
|
+
const newStorefrontPassword = new Map(prev.storefrontPassword);
|
|
1139
|
+
newStorefrontPassword.set(viewId, storefrontPassword);
|
|
1140
|
+
return { storefrontPassword: newStorefrontPassword };
|
|
1141
|
+
}),
|
|
1142
|
+
resetView: (viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1143
|
+
const newDecoded = new Map(prev.decodedPayloads);
|
|
1144
|
+
newDecoded.set(viewId, []);
|
|
1145
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1146
|
+
newEncoded.set(viewId, []);
|
|
1147
|
+
return { decodedPayloads: newDecoded, encodedPayloads: newEncoded };
|
|
1148
|
+
}),
|
|
1149
|
+
copyView: (fromViewId, toViewId) => set((prev) => {
|
|
1150
|
+
const newDecoded = new Map(prev.decodedPayloads);
|
|
1151
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1152
|
+
const newHtmlContent = new Map(prev.htmlContent);
|
|
1153
|
+
const newTargetUrl = new Map(prev.targetUrl);
|
|
1154
|
+
const newRenderMode = new Map(prev.renderMode);
|
|
1155
|
+
const newStorefrontPassword = new Map(prev.storefrontPassword);
|
|
1156
|
+
newDecoded.set(toViewId, prev.decodedPayloads.get(fromViewId) ?? []);
|
|
1157
|
+
newEncoded.set(toViewId, prev.encodedPayloads.get(fromViewId) ?? []);
|
|
1158
|
+
newHtmlContent.set(toViewId, prev.htmlContent.get(fromViewId) ?? '');
|
|
1159
|
+
newTargetUrl.set(toViewId, prev.targetUrl.get(fromViewId) ?? '');
|
|
1160
|
+
newRenderMode.set(toViewId, prev.renderMode.get(fromViewId) ?? 'portal');
|
|
1161
|
+
newStorefrontPassword.set(toViewId, prev.storefrontPassword.get(fromViewId) ?? '');
|
|
1162
|
+
return {
|
|
1163
|
+
decodedPayloads: newDecoded,
|
|
1164
|
+
encodedPayloads: newEncoded,
|
|
1165
|
+
htmlContent: newHtmlContent,
|
|
1166
|
+
targetUrl: newTargetUrl,
|
|
1167
|
+
renderMode: newRenderMode,
|
|
1168
|
+
storefrontPassword: newStorefrontPassword,
|
|
1169
|
+
};
|
|
1170
|
+
}),
|
|
1171
|
+
clearView: (viewId) => set((prev) => {
|
|
1172
|
+
const newDecoded = new Map(prev.decodedPayloads);
|
|
1173
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1174
|
+
const newHtmlContent = new Map(prev.htmlContent);
|
|
1175
|
+
const newTargetUrl = new Map(prev.targetUrl);
|
|
1176
|
+
const newRenderMode = new Map(prev.renderMode);
|
|
1177
|
+
const newStorefrontPassword = new Map(prev.storefrontPassword);
|
|
1178
|
+
newDecoded.delete(viewId);
|
|
1179
|
+
newEncoded.delete(viewId);
|
|
1180
|
+
newHtmlContent.delete(viewId);
|
|
1181
|
+
newTargetUrl.delete(viewId);
|
|
1182
|
+
newRenderMode.delete(viewId);
|
|
1183
|
+
newStorefrontPassword.delete(viewId);
|
|
1184
|
+
return {
|
|
1185
|
+
decodedPayloads: newDecoded,
|
|
1186
|
+
encodedPayloads: newEncoded,
|
|
1187
|
+
htmlContent: newHtmlContent,
|
|
1188
|
+
targetUrl: newTargetUrl,
|
|
1189
|
+
renderMode: newRenderMode,
|
|
1190
|
+
storefrontPassword: newStorefrontPassword,
|
|
1191
|
+
};
|
|
1192
|
+
}),
|
|
1193
|
+
resetAll: () => set(initialState),
|
|
1194
|
+
reset: () => set(initialState),
|
|
1195
|
+
})));
|
|
1068
1196
|
|
|
1069
1197
|
const useHeatmapVizRectStore = create()(subscribeWithSelector((set) => {
|
|
1070
1198
|
return {
|
|
@@ -1170,26 +1298,12 @@ const useHeatmapClickContext = createViewContextHook({
|
|
|
1170
1298
|
}),
|
|
1171
1299
|
});
|
|
1172
1300
|
|
|
1173
|
-
/**
|
|
1174
|
-
* Hook to access heatmap data state and actions with optional selector
|
|
1175
|
-
*
|
|
1176
|
-
* @example
|
|
1177
|
-
* ```tsx
|
|
1178
|
-
* // Get everything
|
|
1179
|
-
* const { data, clickmap, setData } = useHeatmapDataContext();
|
|
1180
|
-
*
|
|
1181
|
-
* // Get only what you need (no unnecessary re-renders)
|
|
1182
|
-
* const data = useHeatmapDataContext(s => s.data);
|
|
1183
|
-
* const { setData, setClickmap } = useHeatmapDataContext(s => ({
|
|
1184
|
-
* setData: s.setData,
|
|
1185
|
-
* setClickmap: s.setClickmap,
|
|
1186
|
-
* }));
|
|
1187
|
-
* ```
|
|
1188
|
-
*/
|
|
1189
1301
|
const useHeatmapDataContext = createViewContextHook({
|
|
1190
1302
|
useStore: useHeatmapDataStore,
|
|
1191
1303
|
getState: (store, viewId) => ({
|
|
1192
1304
|
data: store.data.get(viewId),
|
|
1305
|
+
dataHash: store.dataHash.get(viewId),
|
|
1306
|
+
dataSnapshot: store.dataSnapshot.get(viewId),
|
|
1193
1307
|
clickmap: store.clickmap.get(viewId),
|
|
1194
1308
|
clickAreas: store.clickAreas.get(viewId),
|
|
1195
1309
|
scrollmap: store.scrollmap.get(viewId),
|
|
@@ -1199,6 +1313,8 @@ const useHeatmapDataContext = createViewContextHook({
|
|
|
1199
1313
|
}),
|
|
1200
1314
|
getActions: (store, viewId) => ({
|
|
1201
1315
|
setData: (newData) => store.setData(newData, viewId),
|
|
1316
|
+
setDataSnapshot: (newData) => store.setDataSnapshot(newData, viewId),
|
|
1317
|
+
setDataHash: (newHash) => store.setDataHash(newHash, viewId),
|
|
1202
1318
|
setClickmap: (newClickmap) => store.setClickmap(newClickmap, viewId),
|
|
1203
1319
|
setClickAreas: (newClickAreas) => store.setClickAreas(newClickAreas, viewId),
|
|
1204
1320
|
setDataInfoByKey: (key, value) => store.setDataInfoByKey(key, value, viewId),
|
|
@@ -1221,6 +1337,32 @@ const useHeatmapHoverContext = createViewContextHook({
|
|
|
1221
1337
|
}),
|
|
1222
1338
|
});
|
|
1223
1339
|
|
|
1340
|
+
const useHeatmapLiveContext = createViewContextHook({
|
|
1341
|
+
useStore: useHeatmapLiveStore,
|
|
1342
|
+
getState: (store, viewId) => ({
|
|
1343
|
+
decodedPayloads: store.decodedPayloads.get(viewId) ?? [],
|
|
1344
|
+
encodedPayloads: store.encodedPayloads.get(viewId) ?? [],
|
|
1345
|
+
htmlContent: store.htmlContent.get(viewId) ?? '',
|
|
1346
|
+
targetUrl: store.targetUrl.get(viewId) ?? '',
|
|
1347
|
+
renderMode: store.renderMode.get(viewId) ?? 'portal',
|
|
1348
|
+
storefrontPassword: store.storefrontPassword.get(viewId) ?? '',
|
|
1349
|
+
}),
|
|
1350
|
+
getActions: (store, viewId) => ({
|
|
1351
|
+
addPayload: (payload) => store.addPayload(payload, viewId),
|
|
1352
|
+
setPayloads: (payloads) => store.setPayloads(payloads, viewId),
|
|
1353
|
+
setEncodedPayloads: (payloads) => store.setEncodedPayloads(payloads, viewId),
|
|
1354
|
+
setHtmlContent: (htmlContent) => store.setHtmlContent(htmlContent, viewId),
|
|
1355
|
+
setTargetUrl: (targetUrl) => store.setTargetUrl(targetUrl, viewId),
|
|
1356
|
+
setRenderMode: (mode) => store.setRenderMode(mode, viewId),
|
|
1357
|
+
setStorefrontPassword: (password) => store.setStorefrontPassword(password, viewId),
|
|
1358
|
+
resetView: () => store.resetView(viewId),
|
|
1359
|
+
copyView: (fromViewId, toViewId) => store.copyView(fromViewId, toViewId),
|
|
1360
|
+
clearView: (viewId) => store.clearView(viewId),
|
|
1361
|
+
resetAll: () => store.resetAll(),
|
|
1362
|
+
reset: () => store.reset(),
|
|
1363
|
+
}),
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1224
1366
|
const useHeatmapScrollContext = createViewContextHook({
|
|
1225
1367
|
useStore: useHeatmapVizScrollStore,
|
|
1226
1368
|
getState: (store, viewId) => ({
|
|
@@ -1250,6 +1392,7 @@ const useHeatmapSettingContext = createViewContextHook({
|
|
|
1250
1392
|
clickMode: store.clickMode.get(viewId),
|
|
1251
1393
|
scrollType: store.scrollType.get(viewId),
|
|
1252
1394
|
heatmapType: store.heatmapType.get(viewId),
|
|
1395
|
+
dataSource: store.dataSource.get(viewId) ?? EHeatmapDataSource.Snapshot,
|
|
1253
1396
|
}),
|
|
1254
1397
|
getActions: (store, viewId) => ({
|
|
1255
1398
|
setIsShowSidebar: (isShowSidebar) => store.setIsShowSidebar(isShowSidebar, viewId),
|
|
@@ -1262,6 +1405,7 @@ const useHeatmapSettingContext = createViewContextHook({
|
|
|
1262
1405
|
setIsRendering: (isRendering) => store.setIsRendering(isRendering, viewId),
|
|
1263
1406
|
setIsLoadingDom: (isLoadingDom) => store.setIsLoadingDom(isLoadingDom, viewId),
|
|
1264
1407
|
setIsLoadingCanvas: (isLoadingCanvas) => store.setIsLoadingCanvas(isLoadingCanvas, viewId),
|
|
1408
|
+
setDataSource: (dataSource) => store.setDataSource(dataSource, viewId),
|
|
1265
1409
|
clearView: (viewId) => store.clearView(viewId),
|
|
1266
1410
|
}),
|
|
1267
1411
|
});
|
|
@@ -1269,7 +1413,7 @@ const useHeatmapSettingContext = createViewContextHook({
|
|
|
1269
1413
|
const useHeatmapVizContext = createViewContextHook({
|
|
1270
1414
|
useStore: useHeatmapVizStore,
|
|
1271
1415
|
getState: (store, viewId) => ({
|
|
1272
|
-
|
|
1416
|
+
isRenderedViz: store.isRenderedViz.get(viewId) ?? false,
|
|
1273
1417
|
zoomRatio: store.zoomRatio.get(viewId) ?? DEFAULT_ZOOM_RATIO.DEFAULT,
|
|
1274
1418
|
minZoomRatio: store.minZoomRatio.get(viewId) ?? DEFAULT_ZOOM_RATIO.MIN,
|
|
1275
1419
|
maxZoomRatio: store.maxZoomRatio.get(viewId) ?? DEFAULT_ZOOM_RATIO.MAX,
|
|
@@ -1277,7 +1421,7 @@ const useHeatmapVizContext = createViewContextHook({
|
|
|
1277
1421
|
isScaledToFit: store.isScaledToFit.get(viewId) ?? false,
|
|
1278
1422
|
}),
|
|
1279
1423
|
getActions: (store, viewId) => ({
|
|
1280
|
-
|
|
1424
|
+
setIsRenderedViz: (value) => store.setIsRenderedViz(value, viewId),
|
|
1281
1425
|
setZoomRatio: (value) => store.setZoomRatio(value, viewId),
|
|
1282
1426
|
setMinZoomRatio: (value) => store.setMinZoomRatio(value, viewId),
|
|
1283
1427
|
setMaxZoomRatio: (value) => store.setMaxZoomRatio(value, viewId),
|
|
@@ -1311,9 +1455,7 @@ const useHeatmapCopyView = () => {
|
|
|
1311
1455
|
const copyVizView = useHeatmapVizStore((state) => state.copyView);
|
|
1312
1456
|
const copyVizClickView = useHeatmapVizClickStore((state) => state.copyView);
|
|
1313
1457
|
const copyVizAreaClickView = useHeatmapVizClickAreaStore((state) => state.copyView);
|
|
1314
|
-
|
|
1315
|
-
// const copyVizHoverView = useHeatmapVizHoverStore((state) => state.copyView);
|
|
1316
|
-
// const copyVizScrollView = useHeatmapVizScrollStore((state) => state.copyView);
|
|
1458
|
+
const copyLiveView = useHeatmapLiveStore((state) => state.copyView);
|
|
1317
1459
|
const clearDataView = useHeatmapDataStore((state) => state.clearView);
|
|
1318
1460
|
const clearSettingView = useHeatmapSettingStore((state) => state.clearView);
|
|
1319
1461
|
const clearVizView = useHeatmapVizStore((state) => state.clearView);
|
|
@@ -1322,6 +1464,7 @@ const useHeatmapCopyView = () => {
|
|
|
1322
1464
|
const clearVizHoverView = useHeatmapVizHoverStore((state) => state.clearView);
|
|
1323
1465
|
const clearVizScrollView = useHeatmapVizScrollStore((state) => state.clearView);
|
|
1324
1466
|
const clearVizAreaClickView = useHeatmapVizClickAreaStore((state) => state.clearView);
|
|
1467
|
+
const clearLiveView = useHeatmapLiveStore((state) => state.clearView);
|
|
1325
1468
|
const resetDataAll = useHeatmapDataStore((state) => state.resetAll);
|
|
1326
1469
|
const resetSettingAll = useHeatmapSettingStore((state) => state.resetAll);
|
|
1327
1470
|
const resetVizAll = useHeatmapVizStore((state) => state.resetAll);
|
|
@@ -1330,15 +1473,14 @@ const useHeatmapCopyView = () => {
|
|
|
1330
1473
|
const resetVizHoverAll = useHeatmapVizHoverStore((state) => state.resetAll);
|
|
1331
1474
|
const resetVizScrollViewAll = useHeatmapVizScrollStore((state) => state.resetAll);
|
|
1332
1475
|
const resetVizClickAreaAll = useHeatmapVizClickAreaStore((state) => state.resetAll);
|
|
1476
|
+
const resetLiveAll = useHeatmapLiveStore((state) => state.resetAll);
|
|
1333
1477
|
const copyView = (fromViewId, toViewId) => {
|
|
1334
1478
|
copyDataView(fromViewId, toViewId);
|
|
1335
1479
|
copySettingView(fromViewId, toViewId);
|
|
1336
1480
|
copyVizView(fromViewId, toViewId);
|
|
1337
|
-
// copyVizRectView(fromViewId, toViewId);
|
|
1338
1481
|
copyVizClickView(fromViewId, toViewId);
|
|
1339
|
-
// copyVizHoverView(fromViewId, toViewId);
|
|
1340
|
-
// copyVizScrollView(fromViewId, toViewId);
|
|
1341
1482
|
copyVizAreaClickView(fromViewId, toViewId);
|
|
1483
|
+
copyLiveView(fromViewId, toViewId);
|
|
1342
1484
|
};
|
|
1343
1485
|
const copyViewToMultiple = (fromViewId, toViewIds) => {
|
|
1344
1486
|
toViewIds.forEach((toViewId) => {
|
|
@@ -1354,6 +1496,7 @@ const useHeatmapCopyView = () => {
|
|
|
1354
1496
|
clearVizHoverView(viewId);
|
|
1355
1497
|
clearVizScrollView(viewId);
|
|
1356
1498
|
clearVizAreaClickView(viewId);
|
|
1499
|
+
clearLiveView(viewId);
|
|
1357
1500
|
};
|
|
1358
1501
|
const clearMultipleViews = (viewIds) => {
|
|
1359
1502
|
viewIds.forEach((viewId) => {
|
|
@@ -1369,6 +1512,7 @@ const useHeatmapCopyView = () => {
|
|
|
1369
1512
|
resetVizHoverAll();
|
|
1370
1513
|
resetVizScrollViewAll();
|
|
1371
1514
|
resetVizClickAreaAll();
|
|
1515
|
+
resetLiveAll();
|
|
1372
1516
|
};
|
|
1373
1517
|
return {
|
|
1374
1518
|
copyView,
|
|
@@ -1387,6 +1531,8 @@ const useHeatmapWidthByDevice = () => {
|
|
|
1387
1531
|
if (!deviceType)
|
|
1388
1532
|
return 1440;
|
|
1389
1533
|
switch (deviceType) {
|
|
1534
|
+
case EDeviceType.DesktopLarge:
|
|
1535
|
+
return 1920;
|
|
1390
1536
|
case EDeviceType.Desktop:
|
|
1391
1537
|
return 1440;
|
|
1392
1538
|
case EDeviceType.Tablet:
|
|
@@ -1397,13 +1543,20 @@ const useHeatmapWidthByDevice = () => {
|
|
|
1397
1543
|
}
|
|
1398
1544
|
};
|
|
1399
1545
|
|
|
1400
|
-
const useRegisterConfig = ({ isLoading, isLoadingCanvas, }) => {
|
|
1546
|
+
const useRegisterConfig = ({ shopId, isLoading, isLoadingCanvas, }) => {
|
|
1401
1547
|
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
1548
|
+
const shopIdStore = useHeatmapConfigStore((state) => state.shopId);
|
|
1402
1549
|
const deviceType = useHeatmapSettingContext((state) => state.deviceType);
|
|
1403
1550
|
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
1404
1551
|
const setIsRendering = useHeatmapSettingContext((state) => state.setIsRendering);
|
|
1405
1552
|
const setIsLoadingDom = useHeatmapSettingContext((state) => state.setIsLoadingDom);
|
|
1406
1553
|
const setIsLoadingCanvas = useHeatmapSettingContext((state) => state.setIsLoadingCanvas);
|
|
1554
|
+
const setShopId = useHeatmapConfigStore((state) => state.setShopId);
|
|
1555
|
+
useEffect(() => {
|
|
1556
|
+
if (!shopId || !!shopIdStore || shopIdStore === shopId)
|
|
1557
|
+
return;
|
|
1558
|
+
setShopId(shopId);
|
|
1559
|
+
}, [shopId, setShopId, shopIdStore]);
|
|
1407
1560
|
useEffect(() => {
|
|
1408
1561
|
setIsRendering(true);
|
|
1409
1562
|
setTimeout(() => {
|
|
@@ -1770,7 +1923,7 @@ class Logger {
|
|
|
1770
1923
|
}
|
|
1771
1924
|
}
|
|
1772
1925
|
// Export singleton instance
|
|
1773
|
-
const logger$
|
|
1926
|
+
const logger$4 = new Logger();
|
|
1774
1927
|
// Export factory function để tạo logger với config riêng
|
|
1775
1928
|
function createLogger(config = {}) {
|
|
1776
1929
|
const instance = new Logger();
|
|
@@ -2159,7 +2312,7 @@ function findElementByHash(props) {
|
|
|
2159
2312
|
}
|
|
2160
2313
|
}
|
|
2161
2314
|
catch (error) {
|
|
2162
|
-
logger$
|
|
2315
|
+
logger$4.warn(`Invalid selector "${selector}":`, error);
|
|
2163
2316
|
}
|
|
2164
2317
|
const elementByHash = iframeDocument.querySelector(`[data-clarity-hashalpha="${hash}"], [data-clarity-hash="${hash}"], [data-clarity-hashbeta="${hash}"]`);
|
|
2165
2318
|
return elementByHash;
|
|
@@ -2181,7 +2334,7 @@ function hydrateAreaNode(props) {
|
|
|
2181
2334
|
const { id, hash, selector } = persistedData;
|
|
2182
2335
|
const element = findElementByHash({ hash, selector, iframeDocument, vizRef });
|
|
2183
2336
|
if (!element) {
|
|
2184
|
-
logger$
|
|
2337
|
+
logger$4.warn(`Cannot hydrate area ${id}: element not found for hash ${hash} or selector ${selector}`);
|
|
2185
2338
|
return null;
|
|
2186
2339
|
}
|
|
2187
2340
|
const areaNode = buildAreaNode(element, hash, heatmapInfo, shadowRoot, persistedData);
|
|
@@ -2198,7 +2351,7 @@ function hydrateAreas(props) {
|
|
|
2198
2351
|
hydratedAreas.push(area);
|
|
2199
2352
|
}
|
|
2200
2353
|
}
|
|
2201
|
-
logger$
|
|
2354
|
+
logger$4.info(`Hydrated ${hydratedAreas.length} of ${clickAreas.length} persisted areas`);
|
|
2202
2355
|
return hydratedAreas;
|
|
2203
2356
|
}
|
|
2204
2357
|
/**
|
|
@@ -2902,16 +3055,16 @@ const calcCalloutPositionAbsolute = (props) => {
|
|
|
2902
3055
|
|
|
2903
3056
|
function validateAreaCreation(dataInfo, hash, areas) {
|
|
2904
3057
|
if (!dataInfo?.clickMapMetrics || !dataInfo?.totalClicks) {
|
|
2905
|
-
logger$
|
|
3058
|
+
logger$4.warn('Cannot create area: missing heatmap data');
|
|
2906
3059
|
return false;
|
|
2907
3060
|
}
|
|
2908
3061
|
if (!hash) {
|
|
2909
|
-
logger$
|
|
3062
|
+
logger$4.warn('Cannot create area: missing hash');
|
|
2910
3063
|
return false;
|
|
2911
3064
|
}
|
|
2912
3065
|
const alreadyExists = areas.some((area) => area.hash === hash);
|
|
2913
3066
|
if (alreadyExists) {
|
|
2914
|
-
logger$
|
|
3067
|
+
logger$4.warn(`Area already exists for element: ${hash}`);
|
|
2915
3068
|
return false;
|
|
2916
3069
|
}
|
|
2917
3070
|
return true;
|
|
@@ -2924,14 +3077,14 @@ function identifyConflictingAreas(area) {
|
|
|
2924
3077
|
// Case 1: New area is a child of an existing area
|
|
2925
3078
|
if (area.parentNode) {
|
|
2926
3079
|
conflicts.parentId = area.parentNode.id;
|
|
2927
|
-
logger$
|
|
3080
|
+
logger$4.info(`New area "${area.selector}" is a child of existing area "${area.parentNode.selector}". Will remove parent.`);
|
|
2928
3081
|
}
|
|
2929
3082
|
// Case 2: New area is a parent of existing area(s)
|
|
2930
3083
|
if (area.childNodes.size > 0) {
|
|
2931
3084
|
area.childNodes.forEach((childArea) => {
|
|
2932
3085
|
conflicts.childrenIds.push(childArea.id);
|
|
2933
3086
|
});
|
|
2934
|
-
logger$
|
|
3087
|
+
logger$4.info(`New area "${area.selector}" is a parent of ${area.childNodes.size} existing area(s). Will remove children.`);
|
|
2935
3088
|
}
|
|
2936
3089
|
return conflicts;
|
|
2937
3090
|
}
|
|
@@ -2982,7 +3135,7 @@ function useAreaCreation(options = {}) {
|
|
|
2982
3135
|
}
|
|
2983
3136
|
}
|
|
2984
3137
|
catch (error) {
|
|
2985
|
-
logger$
|
|
3138
|
+
logger$4.error('Failed to create area:', error);
|
|
2986
3139
|
}
|
|
2987
3140
|
}, [dataInfo, areas, addArea, removeArea, removeClickArea, customShadowRoot, onAreaCreated]);
|
|
2988
3141
|
return {
|
|
@@ -3097,16 +3250,16 @@ function useAreaHydration(options) {
|
|
|
3097
3250
|
return;
|
|
3098
3251
|
if (!dataInfo)
|
|
3099
3252
|
return;
|
|
3100
|
-
logger$
|
|
3253
|
+
logger$4.info(`Hydrating ${clickAreas.length} persisted areas...`);
|
|
3101
3254
|
const hydratedAreas = hydrateAreas({ clickAreas, heatmapInfo: dataInfo, vizRef, shadowRoot });
|
|
3102
3255
|
if (!hydratedAreas?.length) {
|
|
3103
|
-
logger$
|
|
3256
|
+
logger$4.warn('No areas could be hydrated - all elements may have been removed from DOM');
|
|
3104
3257
|
return;
|
|
3105
3258
|
}
|
|
3106
3259
|
setIsInitializing(true);
|
|
3107
3260
|
buildAreaGraph(hydratedAreas);
|
|
3108
3261
|
setAreas(hydratedAreas);
|
|
3109
|
-
logger$
|
|
3262
|
+
logger$4.info(`Successfully hydrated ${hydratedAreas.length} areas`);
|
|
3110
3263
|
}, [dataInfo, vizRef, isInitializing, clickAreas]);
|
|
3111
3264
|
useEffect(() => {
|
|
3112
3265
|
if (!enabled)
|
|
@@ -3240,7 +3393,7 @@ function useAreaRectSync(options) {
|
|
|
3240
3393
|
area.rect.update(newRect);
|
|
3241
3394
|
}
|
|
3242
3395
|
catch (error) {
|
|
3243
|
-
logger$
|
|
3396
|
+
logger$4.error(`Failed to update rect for area ${area.id}:`, error);
|
|
3244
3397
|
}
|
|
3245
3398
|
});
|
|
3246
3399
|
buildAreaGraph(areas);
|
|
@@ -3345,9 +3498,9 @@ const useAreaClickmap = () => {
|
|
|
3345
3498
|
const useClickmap = () => {
|
|
3346
3499
|
const vizRef = useHeatmapVizRectContext((s) => s.vizRef);
|
|
3347
3500
|
const clickmap = useHeatmapDataContext((s) => s.clickmap);
|
|
3348
|
-
const
|
|
3501
|
+
const isRenderedViz = useHeatmapVizContext((s) => s.isRenderedViz);
|
|
3349
3502
|
const start = useCallback(() => {
|
|
3350
|
-
if (!vizRef || !clickmap || clickmap.length === 0 || !
|
|
3503
|
+
if (!vizRef || !clickmap || clickmap.length === 0 || !isRenderedViz)
|
|
3351
3504
|
return;
|
|
3352
3505
|
try {
|
|
3353
3506
|
vizRef?.clearmap?.();
|
|
@@ -3356,7 +3509,7 @@ const useClickmap = () => {
|
|
|
3356
3509
|
catch (error) {
|
|
3357
3510
|
console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
|
|
3358
3511
|
}
|
|
3359
|
-
}, [vizRef, clickmap,
|
|
3512
|
+
}, [vizRef, clickmap, isRenderedViz]);
|
|
3360
3513
|
return { start };
|
|
3361
3514
|
};
|
|
3362
3515
|
|
|
@@ -3384,7 +3537,7 @@ const useScrollmap = () => {
|
|
|
3384
3537
|
vizRef?.scrollmap?.(scrollmap);
|
|
3385
3538
|
}
|
|
3386
3539
|
catch (error) {
|
|
3387
|
-
logger$
|
|
3540
|
+
logger$4.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
|
|
3388
3541
|
}
|
|
3389
3542
|
}, [vizRef, scrollmap]);
|
|
3390
3543
|
return { start };
|
|
@@ -3855,7 +4008,7 @@ var MessageType;
|
|
|
3855
4008
|
})(MessageType || (MessageType = {}));
|
|
3856
4009
|
function useVizLiveIframeMsg(options = {}) {
|
|
3857
4010
|
const { trustedOrigins = [], onMessage } = options;
|
|
3858
|
-
const addPayload =
|
|
4011
|
+
const addPayload = useHeatmapLiveContext((s) => s.addPayload);
|
|
3859
4012
|
const [isReady, setIsReady] = useState(false);
|
|
3860
4013
|
const iframeRef = useRef(null);
|
|
3861
4014
|
const isValidOrigin = useCallback((origin) => {
|
|
@@ -3881,7 +4034,7 @@ function useVizLiveIframeMsg(options = {}) {
|
|
|
3881
4034
|
switch (message.type) {
|
|
3882
4035
|
case MessageType.GX_DOM_TRACKING_PAYLOAD:
|
|
3883
4036
|
if (message.payload) {
|
|
3884
|
-
const data =
|
|
4037
|
+
const data = JSON.parse(message.payload);
|
|
3885
4038
|
if (data) {
|
|
3886
4039
|
addPayload(data);
|
|
3887
4040
|
}
|
|
@@ -3904,21 +4057,341 @@ function useVizLiveIframeMsg(options = {}) {
|
|
|
3904
4057
|
};
|
|
3905
4058
|
}
|
|
3906
4059
|
|
|
4060
|
+
/**
|
|
4061
|
+
* Unified performance timing utility.
|
|
4062
|
+
*
|
|
4063
|
+
* Two complementary tools:
|
|
4064
|
+
*
|
|
4065
|
+
* 1. `perf` — global DevTools session recorder.
|
|
4066
|
+
* Stores structured timing in `window.__gemxPerf` for inspection.
|
|
4067
|
+
* Used by the iframe-processor rendering pipeline.
|
|
4068
|
+
*
|
|
4069
|
+
* perf.startSession('render-1');
|
|
4070
|
+
* const t = perf.mark('viewport.run');
|
|
4071
|
+
* perf.measure('viewport.run', t);
|
|
4072
|
+
* perf.endSession();
|
|
4073
|
+
*
|
|
4074
|
+
* 2. `createPerfTimer` — per-module console logger factory.
|
|
4075
|
+
* Logs prefixed timings to the console AND records entries into the
|
|
4076
|
+
* active global session so they appear in `window.__gemxPerf` too.
|
|
4077
|
+
*
|
|
4078
|
+
* const timer = createPerfTimer('Render');
|
|
4079
|
+
* const t0 = timer.mark('start');
|
|
4080
|
+
* await timer.wrap('visualizer.html', () => visualizer.html(...));
|
|
4081
|
+
* timer.measure('total', t0);
|
|
4082
|
+
*/
|
|
4083
|
+
const s = {
|
|
4084
|
+
enabled: true,
|
|
4085
|
+
current: null,
|
|
4086
|
+
sessions: [],
|
|
4087
|
+
maxSessions: 20,
|
|
4088
|
+
};
|
|
4089
|
+
// ── Global singleton functions ────────────────────────────────────────────────
|
|
4090
|
+
function startSession(id) {
|
|
4091
|
+
if (!s.enabled)
|
|
4092
|
+
return;
|
|
4093
|
+
s.current = { id, startedAt: performance.now(), entries: [] };
|
|
4094
|
+
}
|
|
4095
|
+
function endSession() {
|
|
4096
|
+
if (!s.enabled || !s.current)
|
|
4097
|
+
return null;
|
|
4098
|
+
const session = s.current;
|
|
4099
|
+
session.total = performance.now() - session.startedAt;
|
|
4100
|
+
s.sessions = [session, ...s.sessions].slice(0, s.maxSessions);
|
|
4101
|
+
s.current = null;
|
|
4102
|
+
flush();
|
|
4103
|
+
return session;
|
|
4104
|
+
}
|
|
4105
|
+
/** Record a point-in-time mark. Returns `performance.now()` for use with measure(). */
|
|
4106
|
+
function globalMark(label) {
|
|
4107
|
+
const now = performance.now();
|
|
4108
|
+
if (s.enabled && s.current) {
|
|
4109
|
+
s.current.entries.push({ label, t: now - s.current.startedAt });
|
|
4110
|
+
}
|
|
4111
|
+
return now;
|
|
4112
|
+
}
|
|
4113
|
+
/** Record a duration from a previous mark() timestamp. */
|
|
4114
|
+
function globalMeasure(label, t0) {
|
|
4115
|
+
const duration = performance.now() - t0;
|
|
4116
|
+
if (s.enabled && s.current) {
|
|
4117
|
+
s.current.entries.push({ label, t: t0 - s.current.startedAt, duration });
|
|
4118
|
+
}
|
|
4119
|
+
return duration;
|
|
4120
|
+
}
|
|
4121
|
+
function getReport() {
|
|
4122
|
+
return {
|
|
4123
|
+
sessions: s.sessions,
|
|
4124
|
+
latest: s.sessions[0] ?? null,
|
|
4125
|
+
};
|
|
4126
|
+
}
|
|
4127
|
+
function clearSessions() {
|
|
4128
|
+
s.current = null;
|
|
4129
|
+
s.sessions = [];
|
|
4130
|
+
if (typeof window !== 'undefined')
|
|
4131
|
+
delete window.__gemxPerf;
|
|
4132
|
+
}
|
|
4133
|
+
function enableGlobal() {
|
|
4134
|
+
s.enabled = true;
|
|
4135
|
+
}
|
|
4136
|
+
function disableGlobal() {
|
|
4137
|
+
s.enabled = false;
|
|
4138
|
+
}
|
|
4139
|
+
function flush() {
|
|
4140
|
+
if (typeof window === 'undefined')
|
|
4141
|
+
return;
|
|
4142
|
+
window.__gemxPerf = getReport();
|
|
4143
|
+
}
|
|
4144
|
+
// ── Global singleton export ───────────────────────────────────────────────────
|
|
4145
|
+
const perf = {
|
|
4146
|
+
startSession,
|
|
4147
|
+
endSession,
|
|
4148
|
+
mark: globalMark,
|
|
4149
|
+
measure: globalMeasure,
|
|
4150
|
+
getReport,
|
|
4151
|
+
clear: clearSessions,
|
|
4152
|
+
enable: enableGlobal,
|
|
4153
|
+
disable: disableGlobal,
|
|
4154
|
+
};
|
|
4155
|
+
|
|
3907
4156
|
/**
|
|
3908
4157
|
* DOM observation setup — ResizeObserver + MutationObserver.
|
|
3909
4158
|
* Returns a cleanup function that disconnects both observers.
|
|
3910
4159
|
*/
|
|
3911
|
-
createLogger({ enabled: false, prefix: 'IframeHeightObserver' });
|
|
4160
|
+
const logger$3 = createLogger({ enabled: false, prefix: 'IframeHeightObserver' });
|
|
4161
|
+
function setup(doc, onChange) {
|
|
4162
|
+
const resizeObserver = new ResizeObserver(onChange);
|
|
4163
|
+
resizeObserver.observe(doc.documentElement);
|
|
4164
|
+
resizeObserver.observe(doc.body);
|
|
4165
|
+
const mutationObserver = new MutationObserver(onChange);
|
|
4166
|
+
mutationObserver.observe(doc.body, {
|
|
4167
|
+
childList: true,
|
|
4168
|
+
subtree: true,
|
|
4169
|
+
attributes: true,
|
|
4170
|
+
attributeFilter: ['style', 'class', 'hidden', 'data-v'],
|
|
4171
|
+
});
|
|
4172
|
+
logger$3.log('DOM observers started (ResizeObserver + MutationObserver)');
|
|
4173
|
+
return () => {
|
|
4174
|
+
resizeObserver.disconnect();
|
|
4175
|
+
mutationObserver.disconnect();
|
|
4176
|
+
logger$3.log('DOM observers disconnected');
|
|
4177
|
+
};
|
|
4178
|
+
}
|
|
3912
4179
|
|
|
3913
4180
|
/**
|
|
3914
4181
|
* Height Observer Processor
|
|
3915
4182
|
* Background observer — watches for iframe content height changes.
|
|
3916
4183
|
*/
|
|
3917
|
-
|
|
4184
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
4185
|
+
function clearTimers(s) {
|
|
4186
|
+
if (s.throttleTimeout) {
|
|
4187
|
+
clearTimeout(s.throttleTimeout);
|
|
4188
|
+
s.throttleTimeout = null;
|
|
4189
|
+
}
|
|
4190
|
+
if (s.debounceTimeout) {
|
|
4191
|
+
clearTimeout(s.debounceTimeout);
|
|
4192
|
+
s.debounceTimeout = null;
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
function getActualHeight(s) {
|
|
4196
|
+
if (!s.iframe?.contentDocument)
|
|
4197
|
+
return 0;
|
|
4198
|
+
const { documentElement: docEl, body } = s.iframe.contentDocument;
|
|
4199
|
+
const heights = [docEl.scrollHeight, docEl.offsetHeight, body.scrollHeight, body.offsetHeight];
|
|
4200
|
+
const maxHeight = Math.max(...heights.filter((h) => h > 0));
|
|
4201
|
+
s.logger.log('Height sources:', {
|
|
4202
|
+
'documentElement.scrollHeight': docEl.scrollHeight,
|
|
4203
|
+
'documentElement.offsetHeight': docEl.offsetHeight,
|
|
4204
|
+
'body.scrollHeight': body.scrollHeight,
|
|
4205
|
+
'body.offsetHeight': body.offsetHeight,
|
|
4206
|
+
maxHeight,
|
|
4207
|
+
});
|
|
4208
|
+
return maxHeight;
|
|
4209
|
+
}
|
|
4210
|
+
async function processHeightChange(s, newHeight) {
|
|
4211
|
+
if (!s.iframe || !s.config)
|
|
4212
|
+
return;
|
|
4213
|
+
s.isProcessing = true;
|
|
4214
|
+
s.logger.log(`Processing height change: ${newHeight}px`);
|
|
4215
|
+
try {
|
|
4216
|
+
const result = {
|
|
4217
|
+
height: newHeight,
|
|
4218
|
+
width: s.iframe.contentWindow?.innerWidth ?? 0,
|
|
4219
|
+
};
|
|
4220
|
+
s.lastHeight = newHeight;
|
|
4221
|
+
s.logger.log('Height change processed:', result);
|
|
4222
|
+
s.config.onHeightChange?.(result);
|
|
4223
|
+
window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', { detail: result }));
|
|
4224
|
+
}
|
|
4225
|
+
catch (error) {
|
|
4226
|
+
s.logger.error('Failed to process height change:', error);
|
|
4227
|
+
s.config.onError?.(error);
|
|
4228
|
+
}
|
|
4229
|
+
finally {
|
|
4230
|
+
s.isProcessing = false;
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
function handleHeightChange(s) {
|
|
4234
|
+
if (s.isProcessing || s.throttleTimeout)
|
|
4235
|
+
return;
|
|
4236
|
+
s.throttleTimeout = setTimeout(() => {
|
|
4237
|
+
s.throttleTimeout = null;
|
|
4238
|
+
const currentHeight = getActualHeight(s);
|
|
4239
|
+
if (currentHeight === s.lastHeight)
|
|
4240
|
+
return;
|
|
4241
|
+
s.logger.log(`Height changed: ${s.lastHeight}px -> ${currentHeight}px`);
|
|
4242
|
+
if (s.debounceTimeout)
|
|
4243
|
+
clearTimeout(s.debounceTimeout);
|
|
4244
|
+
s.debounceTimeout = setTimeout(() => {
|
|
4245
|
+
s.debounceTimeout = null;
|
|
4246
|
+
processHeightChange(s, currentHeight);
|
|
4247
|
+
}, s.debounceMs);
|
|
4248
|
+
}, s.throttleMs);
|
|
4249
|
+
}
|
|
4250
|
+
function observe(s) {
|
|
4251
|
+
if (!s.iframe?.contentDocument?.body) {
|
|
4252
|
+
s.logger.warn('Cannot observe height changes: iframe body not found');
|
|
4253
|
+
return;
|
|
4254
|
+
}
|
|
4255
|
+
s.observerCleanup?.();
|
|
4256
|
+
s.lastHeight = s.iframe.contentDocument.documentElement.scrollHeight;
|
|
4257
|
+
s.logger.log('Initial height:', s.lastHeight);
|
|
4258
|
+
s.observerCleanup = setup(s.iframe.contentDocument, () => handleHeightChange(s));
|
|
4259
|
+
}
|
|
4260
|
+
function start$5(s, cfg) {
|
|
4261
|
+
if (s.running) {
|
|
4262
|
+
s.logger.warn('Observer is already running. Call stop() first.');
|
|
4263
|
+
return;
|
|
4264
|
+
}
|
|
4265
|
+
s.iframe = cfg.iframe;
|
|
4266
|
+
s.config = cfg;
|
|
4267
|
+
s.throttleMs = cfg.throttleMs ?? 25;
|
|
4268
|
+
s.debounceMs = cfg.debounceMs ?? 500;
|
|
4269
|
+
s.running = true;
|
|
4270
|
+
observe(s);
|
|
4271
|
+
s.logger.log('Height observer started');
|
|
4272
|
+
}
|
|
4273
|
+
function stop$5(s) {
|
|
4274
|
+
if (!s.running)
|
|
4275
|
+
return;
|
|
4276
|
+
s.observerCleanup?.();
|
|
4277
|
+
s.observerCleanup = null;
|
|
4278
|
+
clearTimers(s);
|
|
4279
|
+
s.iframe = null;
|
|
4280
|
+
s.config = null;
|
|
4281
|
+
s.lastHeight = 0;
|
|
4282
|
+
s.isProcessing = false;
|
|
4283
|
+
s.running = false;
|
|
4284
|
+
s.logger.log('Height observer stopped');
|
|
4285
|
+
}
|
|
4286
|
+
function clear(s) {
|
|
4287
|
+
s.observerCleanup?.();
|
|
4288
|
+
s.observerCleanup = null;
|
|
4289
|
+
clearTimers(s);
|
|
4290
|
+
s.lastHeight = 0;
|
|
4291
|
+
s.isProcessing = false;
|
|
4292
|
+
}
|
|
4293
|
+
function updateConfig$3(s, cfg) {
|
|
4294
|
+
if (!s.running || !s.config) {
|
|
4295
|
+
s.logger.warn('Observer is not running.');
|
|
4296
|
+
return;
|
|
4297
|
+
}
|
|
4298
|
+
s.config = { ...s.config, ...cfg };
|
|
4299
|
+
if (cfg.throttleMs !== undefined)
|
|
4300
|
+
s.throttleMs = cfg.throttleMs;
|
|
4301
|
+
if (cfg.debounceMs !== undefined)
|
|
4302
|
+
s.debounceMs = cfg.debounceMs;
|
|
4303
|
+
s.logger.configure({ enabled: !!s.config.debug });
|
|
4304
|
+
s.logger.log('Config updated');
|
|
4305
|
+
}
|
|
4306
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
4307
|
+
function createHeightObserver() {
|
|
4308
|
+
const s = {
|
|
4309
|
+
logger: createLogger({ enabled: true, prefix: 'IframeHeightObserver' }),
|
|
4310
|
+
iframe: null,
|
|
4311
|
+
config: null,
|
|
4312
|
+
observerCleanup: null,
|
|
4313
|
+
lastHeight: 0,
|
|
4314
|
+
throttleTimeout: null,
|
|
4315
|
+
debounceTimeout: null,
|
|
4316
|
+
isProcessing: false,
|
|
4317
|
+
throttleMs: 25,
|
|
4318
|
+
debounceMs: 500,
|
|
4319
|
+
running: false,
|
|
4320
|
+
};
|
|
4321
|
+
return {
|
|
4322
|
+
start: (cfg) => start$5(s, cfg),
|
|
4323
|
+
stop: () => stop$5(s),
|
|
4324
|
+
observe: () => observe(s),
|
|
4325
|
+
clear: () => clear(s),
|
|
4326
|
+
updateConfig: (cfg) => updateConfig$3(s, cfg),
|
|
4327
|
+
getCurrentHeight: () => s.lastHeight,
|
|
4328
|
+
isRunning: () => s.running,
|
|
4329
|
+
getStateInfo: () => ({
|
|
4330
|
+
isRunning: s.running,
|
|
4331
|
+
lastHeight: s.lastHeight,
|
|
4332
|
+
isProcessing: s.isProcessing,
|
|
4333
|
+
hasObservers: !!s.observerCleanup,
|
|
4334
|
+
}),
|
|
4335
|
+
};
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4338
|
+
/**
|
|
4339
|
+
* Window-level event management for the navigation processor.
|
|
4340
|
+
*
|
|
4341
|
+
* Responsibilities:
|
|
4342
|
+
* - Subscribe to events dispatched by this processor (for logging/hooks)
|
|
4343
|
+
* - Dispatch navigation events to the parent window
|
|
4344
|
+
*/
|
|
4345
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
4346
|
+
function attach$1(s, debug) {
|
|
4347
|
+
s.logger.configure({ enabled: !!debug });
|
|
4348
|
+
s.navigationBlockedListener = (e) => {
|
|
4349
|
+
const ev = e;
|
|
4350
|
+
s.logger.log('Navigation blocked:', ev.detail.url);
|
|
4351
|
+
};
|
|
4352
|
+
s.formSubmitWindowListener = (e) => {
|
|
4353
|
+
const ev = e;
|
|
4354
|
+
s.logger.log('Form submitted:', ev.detail.data);
|
|
4355
|
+
};
|
|
4356
|
+
window.addEventListener('iframe-navigation-blocked', s.navigationBlockedListener);
|
|
4357
|
+
window.addEventListener('iframe-form-submit', s.formSubmitWindowListener);
|
|
4358
|
+
}
|
|
4359
|
+
function detach$1(s) {
|
|
4360
|
+
if (s.navigationBlockedListener) {
|
|
4361
|
+
window.removeEventListener('iframe-navigation-blocked', s.navigationBlockedListener);
|
|
4362
|
+
s.navigationBlockedListener = null;
|
|
4363
|
+
}
|
|
4364
|
+
if (s.formSubmitWindowListener) {
|
|
4365
|
+
window.removeEventListener('iframe-form-submit', s.formSubmitWindowListener);
|
|
4366
|
+
s.formSubmitWindowListener = null;
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4369
|
+
function dispatchBlocked(url, showMessage) {
|
|
4370
|
+
if (showMessage)
|
|
4371
|
+
alert(`Navigation blocked: ${url}`);
|
|
4372
|
+
window.dispatchEvent(new CustomEvent('iframe-navigation-blocked', { detail: { url } }));
|
|
4373
|
+
}
|
|
4374
|
+
function dispatchFormSubmit(form, data) {
|
|
4375
|
+
window.dispatchEvent(new CustomEvent('iframe-form-submit', { detail: { form, data } }));
|
|
4376
|
+
}
|
|
4377
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
4378
|
+
function createNavigationListeners() {
|
|
4379
|
+
const s = {
|
|
4380
|
+
logger: createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' }),
|
|
4381
|
+
navigationBlockedListener: null,
|
|
4382
|
+
formSubmitWindowListener: null,
|
|
4383
|
+
};
|
|
4384
|
+
return {
|
|
4385
|
+
attach: (debug) => attach$1(s, debug),
|
|
4386
|
+
detach: () => detach$1(s),
|
|
4387
|
+
dispatchBlocked,
|
|
4388
|
+
dispatchFormSubmit,
|
|
4389
|
+
};
|
|
4390
|
+
}
|
|
3918
4391
|
|
|
3919
|
-
const logger$
|
|
4392
|
+
const logger$2 = createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' });
|
|
3920
4393
|
function configure$1(debug) {
|
|
3921
|
-
logger$
|
|
4394
|
+
logger$2.configure({ enabled: debug });
|
|
3922
4395
|
}
|
|
3923
4396
|
// ─── DOM Utilities ────────────────────────────────────────────────────────────
|
|
3924
4397
|
function disableAllLinks(doc) {
|
|
@@ -3942,10 +4415,10 @@ function setupLinkBlocker(doc, isEnabled, onBlocked) {
|
|
|
3942
4415
|
return;
|
|
3943
4416
|
const href = link.getAttribute('href');
|
|
3944
4417
|
if (!href || href === '' || href === '#' || href.startsWith('#')) {
|
|
3945
|
-
logger$
|
|
4418
|
+
logger$2.log('Allowed hash navigation:', href);
|
|
3946
4419
|
return;
|
|
3947
4420
|
}
|
|
3948
|
-
logger$
|
|
4421
|
+
logger$2.log('Blocked link navigation to:', href);
|
|
3949
4422
|
e.preventDefault();
|
|
3950
4423
|
e.stopPropagation();
|
|
3951
4424
|
e.stopImmediatePropagation();
|
|
@@ -3959,7 +4432,7 @@ function setupLinkBlocker(doc, isEnabled, onBlocked) {
|
|
|
3959
4432
|
return;
|
|
3960
4433
|
const href = link.getAttribute('href');
|
|
3961
4434
|
if (href && !href.startsWith('#')) {
|
|
3962
|
-
logger$
|
|
4435
|
+
logger$2.log('Blocked auxclick navigation');
|
|
3963
4436
|
e.preventDefault();
|
|
3964
4437
|
e.stopPropagation();
|
|
3965
4438
|
e.stopImmediatePropagation();
|
|
@@ -3980,7 +4453,7 @@ function setupFormBlocker(doc, isEnabled, onBlocked, onFormSubmit) {
|
|
|
3980
4453
|
const form = e.target;
|
|
3981
4454
|
const action = form.getAttribute('action');
|
|
3982
4455
|
if (!action || action === '' || action === '#') {
|
|
3983
|
-
logger$
|
|
4456
|
+
logger$2.log('Allowed same-page form');
|
|
3984
4457
|
e.preventDefault();
|
|
3985
4458
|
const data = {};
|
|
3986
4459
|
new FormData(form).forEach((value, key) => {
|
|
@@ -3989,7 +4462,7 @@ function setupFormBlocker(doc, isEnabled, onBlocked, onFormSubmit) {
|
|
|
3989
4462
|
onFormSubmit(form, data);
|
|
3990
4463
|
return;
|
|
3991
4464
|
}
|
|
3992
|
-
logger$
|
|
4465
|
+
logger$2.log('Blocked form submission to:', action);
|
|
3993
4466
|
e.preventDefault();
|
|
3994
4467
|
e.stopPropagation();
|
|
3995
4468
|
e.stopImmediatePropagation();
|
|
@@ -4003,7 +4476,7 @@ function setupWindowOpenBlocker(win, originalOpen, isEnabled, onBlocked) {
|
|
|
4003
4476
|
if (!isEnabled())
|
|
4004
4477
|
return originalOpen(...args);
|
|
4005
4478
|
const url = args[0]?.toString() || 'popup';
|
|
4006
|
-
logger$
|
|
4479
|
+
logger$2.log('Blocked window.open:', url);
|
|
4007
4480
|
onBlocked(url);
|
|
4008
4481
|
return null;
|
|
4009
4482
|
});
|
|
@@ -4015,14 +4488,14 @@ function setupUnloadBlocker(win, isEnabled) {
|
|
|
4015
4488
|
const beforeUnloadListener = (e) => {
|
|
4016
4489
|
if (!isEnabled())
|
|
4017
4490
|
return;
|
|
4018
|
-
logger$
|
|
4491
|
+
logger$2.log('Blocked beforeunload');
|
|
4019
4492
|
e.preventDefault();
|
|
4020
4493
|
e.returnValue = '';
|
|
4021
4494
|
};
|
|
4022
4495
|
const unloadListener = (e) => {
|
|
4023
4496
|
if (!isEnabled())
|
|
4024
4497
|
return;
|
|
4025
|
-
logger$
|
|
4498
|
+
logger$2.log('Blocked unload');
|
|
4026
4499
|
e.preventDefault();
|
|
4027
4500
|
e.stopPropagation();
|
|
4028
4501
|
};
|
|
@@ -4039,65 +4512,14 @@ function setupDOMMonitor(doc) {
|
|
|
4039
4512
|
return () => observer.disconnect();
|
|
4040
4513
|
}
|
|
4041
4514
|
|
|
4042
|
-
/**
|
|
4043
|
-
* Window-level event management for the navigation processor.
|
|
4044
|
-
*
|
|
4045
|
-
* Responsibilities:
|
|
4046
|
-
* - Subscribe to events dispatched by this processor (for logging/hooks)
|
|
4047
|
-
* - Dispatch navigation events to the parent window
|
|
4048
|
-
*/
|
|
4049
|
-
const logger$7 = createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' });
|
|
4050
|
-
// ─── State ────────────────────────────────────────────────────────────────────
|
|
4051
|
-
let navigationBlockedListener = null;
|
|
4052
|
-
let formSubmitWindowListener = null;
|
|
4053
|
-
// ─── Subscriptions ────────────────────────────────────────────────────────────
|
|
4054
|
-
function attach$1(debug) {
|
|
4055
|
-
logger$7.configure({ enabled: !!debug });
|
|
4056
|
-
navigationBlockedListener = (e) => {
|
|
4057
|
-
const ev = e;
|
|
4058
|
-
logger$7.log('Navigation blocked:', ev.detail.url);
|
|
4059
|
-
};
|
|
4060
|
-
formSubmitWindowListener = (e) => {
|
|
4061
|
-
const ev = e;
|
|
4062
|
-
logger$7.log('Form submitted:', ev.detail.data);
|
|
4063
|
-
};
|
|
4064
|
-
window.addEventListener('iframe-navigation-blocked', navigationBlockedListener);
|
|
4065
|
-
window.addEventListener('iframe-form-submit', formSubmitWindowListener);
|
|
4066
|
-
}
|
|
4067
|
-
function detach$1() {
|
|
4068
|
-
if (navigationBlockedListener) {
|
|
4069
|
-
window.removeEventListener('iframe-navigation-blocked', navigationBlockedListener);
|
|
4070
|
-
navigationBlockedListener = null;
|
|
4071
|
-
}
|
|
4072
|
-
if (formSubmitWindowListener) {
|
|
4073
|
-
window.removeEventListener('iframe-form-submit', formSubmitWindowListener);
|
|
4074
|
-
formSubmitWindowListener = null;
|
|
4075
|
-
}
|
|
4076
|
-
}
|
|
4077
|
-
// ─── Dispatchers ─────────────────────────────────────────────────────────────
|
|
4078
|
-
function dispatchBlocked(url, showMessage) {
|
|
4079
|
-
if (showMessage)
|
|
4080
|
-
alert(`Navigation blocked: ${url}`);
|
|
4081
|
-
window.dispatchEvent(new CustomEvent('iframe-navigation-blocked', { detail: { url } }));
|
|
4082
|
-
}
|
|
4083
|
-
function dispatchFormSubmit(form, data) {
|
|
4084
|
-
window.dispatchEvent(new CustomEvent('iframe-form-submit', { detail: { form, data } }));
|
|
4085
|
-
}
|
|
4086
|
-
|
|
4087
4515
|
/**
|
|
4088
4516
|
* Navigation Processor
|
|
4089
4517
|
* Continuous guard — blocks all navigation attempts within the iframe.
|
|
4090
4518
|
*/
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
let running$4 = false;
|
|
4096
|
-
let cleanups = [];
|
|
4097
|
-
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
4098
|
-
function start$5(iframe, cfg) {
|
|
4099
|
-
if (running$4) {
|
|
4100
|
-
logger$6.warn('Blocker is already running. Call stop() first.');
|
|
4519
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
4520
|
+
function start$4(s, iframe, cfg) {
|
|
4521
|
+
if (s.running) {
|
|
4522
|
+
s.logger.warn('Blocker is already running. Call stop() first.');
|
|
4101
4523
|
return;
|
|
4102
4524
|
}
|
|
4103
4525
|
if (!iframe.contentDocument || !iframe.contentWindow) {
|
|
@@ -4106,76 +4528,114 @@ function start$5(iframe, cfg) {
|
|
|
4106
4528
|
const doc = iframe.contentDocument;
|
|
4107
4529
|
const win = iframe.contentWindow;
|
|
4108
4530
|
const originalOpen = win.open.bind(win);
|
|
4109
|
-
logger
|
|
4531
|
+
s.logger.configure({ enabled: !!cfg?.debug });
|
|
4110
4532
|
configure$1(!!cfg?.debug);
|
|
4111
|
-
cleanups = [
|
|
4112
|
-
setupLinkBlocker(doc, () => isEnabled, (url) => dispatchBlocked(url, showMessage)),
|
|
4113
|
-
setupFormBlocker(doc, () => isEnabled, (url) => dispatchBlocked(url, showMessage), dispatchFormSubmit),
|
|
4114
|
-
setupWindowOpenBlocker(win, originalOpen, () => isEnabled, (url) => dispatchBlocked(url, showMessage)),
|
|
4115
|
-
setupUnloadBlocker(win, () => isEnabled),
|
|
4533
|
+
s.cleanups = [
|
|
4534
|
+
setupLinkBlocker(doc, () => s.isEnabled, (url) => s.listeners.dispatchBlocked(url, s.showMessage)),
|
|
4535
|
+
setupFormBlocker(doc, () => s.isEnabled, (url) => s.listeners.dispatchBlocked(url, s.showMessage), s.listeners.dispatchFormSubmit),
|
|
4536
|
+
setupWindowOpenBlocker(win, originalOpen, () => s.isEnabled, (url) => s.listeners.dispatchBlocked(url, s.showMessage)),
|
|
4537
|
+
setupUnloadBlocker(win, () => s.isEnabled),
|
|
4116
4538
|
setupDOMMonitor(doc),
|
|
4117
4539
|
];
|
|
4118
|
-
attach
|
|
4119
|
-
running
|
|
4120
|
-
logger
|
|
4540
|
+
s.listeners.attach(cfg?.debug);
|
|
4541
|
+
s.running = true;
|
|
4542
|
+
s.logger.log('Navigation blocker started');
|
|
4543
|
+
}
|
|
4544
|
+
function stop$4(s) {
|
|
4545
|
+
if (!s.running)
|
|
4546
|
+
return;
|
|
4547
|
+
s.cleanups.forEach((fn) => fn());
|
|
4548
|
+
s.cleanups = [];
|
|
4549
|
+
s.listeners.detach();
|
|
4550
|
+
s.isEnabled = false;
|
|
4551
|
+
s.showMessage = false;
|
|
4552
|
+
s.running = false;
|
|
4553
|
+
s.logger.log('Navigation blocker stopped');
|
|
4554
|
+
}
|
|
4555
|
+
function enable(s) {
|
|
4556
|
+
if (!s.running) {
|
|
4557
|
+
s.logger.warn('Blocker is not running.');
|
|
4558
|
+
return;
|
|
4559
|
+
}
|
|
4560
|
+
s.isEnabled = true;
|
|
4561
|
+
s.logger.log('Navigation blocking enabled');
|
|
4121
4562
|
}
|
|
4122
|
-
function
|
|
4123
|
-
if (!running
|
|
4563
|
+
function disable(s) {
|
|
4564
|
+
if (!s.running) {
|
|
4565
|
+
s.logger.warn('Blocker is not running.');
|
|
4124
4566
|
return;
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
running
|
|
4131
|
-
|
|
4132
|
-
}
|
|
4133
|
-
function enable() {
|
|
4134
|
-
if (!running$4) {
|
|
4135
|
-
logger$6.warn('Blocker is not running. Call start() first.');
|
|
4567
|
+
}
|
|
4568
|
+
s.isEnabled = false;
|
|
4569
|
+
s.logger.log('Navigation blocking disabled');
|
|
4570
|
+
}
|
|
4571
|
+
function enableMessage(s) {
|
|
4572
|
+
if (!s.running) {
|
|
4573
|
+
s.logger.warn('Blocker is not running.');
|
|
4136
4574
|
return;
|
|
4137
4575
|
}
|
|
4138
|
-
|
|
4139
|
-
logger
|
|
4576
|
+
s.showMessage = true;
|
|
4577
|
+
s.logger.log('Navigation blocking message enabled');
|
|
4578
|
+
}
|
|
4579
|
+
function disableMessage(s) {
|
|
4580
|
+
if (!s.running) {
|
|
4581
|
+
s.logger.warn('Blocker is not running.');
|
|
4582
|
+
return;
|
|
4583
|
+
}
|
|
4584
|
+
s.showMessage = false;
|
|
4585
|
+
s.logger.log('Navigation blocking message disabled');
|
|
4586
|
+
}
|
|
4587
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
4588
|
+
function createNavigationBlocker() {
|
|
4589
|
+
const s = {
|
|
4590
|
+
logger: createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' }),
|
|
4591
|
+
listeners: createNavigationListeners(),
|
|
4592
|
+
isEnabled: false,
|
|
4593
|
+
showMessage: false,
|
|
4594
|
+
running: false,
|
|
4595
|
+
cleanups: [],
|
|
4596
|
+
};
|
|
4597
|
+
return {
|
|
4598
|
+
start: (iframe, cfg) => start$4(s, iframe, cfg),
|
|
4599
|
+
stop: () => stop$4(s),
|
|
4600
|
+
enable: () => enable(s),
|
|
4601
|
+
disable: () => disable(s),
|
|
4602
|
+
enableMessage: () => enableMessage(s),
|
|
4603
|
+
disableMessage: () => disableMessage(s),
|
|
4604
|
+
isRunning: () => s.running,
|
|
4605
|
+
isBlockingEnabled: () => s.isEnabled,
|
|
4606
|
+
getStateInfo: () => ({ isRunning: s.running, isEnabled: s.isEnabled, showMessage: s.showMessage }),
|
|
4607
|
+
};
|
|
4140
4608
|
}
|
|
4141
4609
|
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4610
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
4611
|
+
function attach(s, debug) {
|
|
4612
|
+
s.logger.configure({ enabled: !!debug });
|
|
4613
|
+
s.dimensionsListener = (e) => {
|
|
4614
|
+
const ev = e;
|
|
4615
|
+
s.logger.log('Dimensions applied:', ev.detail);
|
|
4616
|
+
};
|
|
4617
|
+
window.addEventListener('iframe-dimensions-applied', s.dimensionsListener);
|
|
4149
4618
|
}
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4619
|
+
function detach(s) {
|
|
4620
|
+
if (s.dimensionsListener) {
|
|
4621
|
+
window.removeEventListener('iframe-dimensions-applied', s.dimensionsListener);
|
|
4622
|
+
s.dimensionsListener = null;
|
|
4623
|
+
}
|
|
4624
|
+
}
|
|
4625
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
4626
|
+
function createViewportListeners() {
|
|
4627
|
+
const s = {
|
|
4628
|
+
logger: createLogger({ enabled: false, prefix: 'ViewportReplacer' }),
|
|
4629
|
+
dimensionsListener: null,
|
|
4630
|
+
};
|
|
4631
|
+
return {
|
|
4632
|
+
attach: (debug) => attach(s, debug),
|
|
4633
|
+
detach: () => detach(s),
|
|
4634
|
+
};
|
|
4163
4635
|
}
|
|
4164
4636
|
|
|
4165
|
-
/**
|
|
4166
|
-
* Computed Style Enforcer Module
|
|
4167
|
-
* Enforces computed CSS styles with viewport unit verification
|
|
4168
|
-
* @module computed-style-enforcer
|
|
4169
|
-
*/
|
|
4170
|
-
const logger$5 = createLogger({
|
|
4171
|
-
enabled: false,
|
|
4172
|
-
prefix: 'ComputedStyleEnforcer',
|
|
4173
|
-
});
|
|
4174
|
-
// ============================================================================
|
|
4175
|
-
// Constants
|
|
4176
|
-
// ============================================================================
|
|
4177
4637
|
const DEFAULT_TOLERANCE_PX = 5;
|
|
4178
|
-
const VIEWPORT_UNIT_REGEX = /([
|
|
4638
|
+
const VIEWPORT_UNIT_REGEX = /([-.\\d]+)(vh|svh|lvh|dvh|%)/gi;
|
|
4179
4639
|
const DEFAULT_CSS_VALUES = ['none', 'auto', 'normal', '0px'];
|
|
4180
4640
|
const CRITICAL_PROPERTIES = [
|
|
4181
4641
|
'display',
|
|
@@ -4212,235 +4672,260 @@ const CRITICAL_PROPERTIES = [
|
|
|
4212
4672
|
'grid-template-rows',
|
|
4213
4673
|
'gap',
|
|
4214
4674
|
];
|
|
4215
|
-
|
|
4216
|
-
//
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
let win$1 = null;
|
|
4220
|
-
let config$2 = null;
|
|
4221
|
-
const elementsWithViewportUnits$1 = new Set();
|
|
4222
|
-
let originalValues$1 = new WeakMap();
|
|
4223
|
-
let running$3 = false;
|
|
4224
|
-
// ============================================================================
|
|
4225
|
-
// Helper Functions
|
|
4226
|
-
// ============================================================================
|
|
4227
|
-
/**
|
|
4228
|
-
* Get viewport unit map for conversion from current state
|
|
4229
|
-
*/
|
|
4230
|
-
function getViewportUnitMap() {
|
|
4231
|
-
if (!config$2) {
|
|
4675
|
+
|
|
4676
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
4677
|
+
function getViewportUnitMap(s) {
|
|
4678
|
+
if (!s.config)
|
|
4232
4679
|
throw new Error('Config is not initialized');
|
|
4233
|
-
}
|
|
4234
4680
|
return {
|
|
4235
|
-
vh: config
|
|
4236
|
-
svh: config
|
|
4237
|
-
lvh: config
|
|
4238
|
-
dvh: config
|
|
4239
|
-
vw: config
|
|
4240
|
-
svw: config
|
|
4241
|
-
lvw: config
|
|
4242
|
-
dvw: config
|
|
4681
|
+
vh: s.config.targetHeight,
|
|
4682
|
+
svh: s.config.targetHeight,
|
|
4683
|
+
lvh: s.config.targetHeight,
|
|
4684
|
+
dvh: s.config.targetHeight,
|
|
4685
|
+
vw: s.config.targetWidth,
|
|
4686
|
+
svw: s.config.targetWidth,
|
|
4687
|
+
lvw: s.config.targetWidth,
|
|
4688
|
+
dvw: s.config.targetWidth,
|
|
4243
4689
|
};
|
|
4244
4690
|
}
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
*/
|
|
4248
|
-
function calculateExpectedPx(value, unit) {
|
|
4249
|
-
if (!config$2) {
|
|
4691
|
+
function calculateExpectedPx(s, value, unit) {
|
|
4692
|
+
if (!s.config)
|
|
4250
4693
|
throw new Error('Config is not initialized');
|
|
4251
|
-
}
|
|
4252
4694
|
const unitLower = unit.toLowerCase();
|
|
4253
|
-
if (unitLower === '%')
|
|
4254
|
-
return (value / 100) * config
|
|
4255
|
-
|
|
4256
|
-
const unitMap = getViewportUnitMap();
|
|
4257
|
-
return (value / 100) * (unitMap[unitLower] || 0);
|
|
4695
|
+
if (unitLower === '%')
|
|
4696
|
+
return (value / 100) * s.config.targetHeight;
|
|
4697
|
+
return (value / 100) * (getViewportUnitMap(s)[unitLower] || 0);
|
|
4258
4698
|
}
|
|
4259
|
-
/**
|
|
4260
|
-
* Check if a CSS value is a default/initial value that should be skipped
|
|
4261
|
-
*/
|
|
4262
4699
|
function isDefaultCssValue(value) {
|
|
4263
4700
|
return DEFAULT_CSS_VALUES.includes(value);
|
|
4264
4701
|
}
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
* Return true ONLY if computed value matches target config (within tolerance)
|
|
4268
|
-
* Return false if computed value is different - don't override correct values
|
|
4269
|
-
*/
|
|
4270
|
-
function shouldReplaceValue(computedValue, originalValue, tolerance = DEFAULT_TOLERANCE_PX) {
|
|
4271
|
-
if (!config$2) {
|
|
4702
|
+
function shouldReplaceValue(s, computedValue, originalValue, tolerance = DEFAULT_TOLERANCE_PX) {
|
|
4703
|
+
if (!s.config)
|
|
4272
4704
|
return false;
|
|
4273
|
-
}
|
|
4274
|
-
// Parse computed value (should be in px)
|
|
4275
4705
|
const computedPx = parseFloat(computedValue);
|
|
4276
|
-
if (isNaN(computedPx))
|
|
4277
|
-
return false;
|
|
4278
|
-
}
|
|
4279
|
-
// Parse original value to check what it should be
|
|
4706
|
+
if (isNaN(computedPx))
|
|
4707
|
+
return false;
|
|
4280
4708
|
const regex = new RegExp(VIEWPORT_UNIT_REGEX.source, VIEWPORT_UNIT_REGEX.flags);
|
|
4281
4709
|
const match = originalValue.match(regex);
|
|
4282
|
-
if (!match)
|
|
4283
|
-
return false;
|
|
4284
|
-
}
|
|
4710
|
+
if (!match)
|
|
4711
|
+
return false;
|
|
4285
4712
|
const [, value, unit] = match;
|
|
4286
4713
|
const num = parseFloat(value);
|
|
4287
|
-
if (isNaN(num))
|
|
4714
|
+
if (isNaN(num))
|
|
4288
4715
|
return false;
|
|
4289
|
-
|
|
4290
|
-
// Calculate expected value based on unit and target config
|
|
4291
|
-
const expectedPx = calculateExpectedPx(num, unit);
|
|
4292
|
-
// Check if computed value matches expected value (within tolerance)
|
|
4716
|
+
const expectedPx = calculateExpectedPx(s, num, unit);
|
|
4293
4717
|
const diff = Math.abs(computedPx - expectedPx);
|
|
4294
4718
|
if (diff <= tolerance) {
|
|
4295
|
-
|
|
4296
|
-
logger$5.log(`OK to replace: computed=${computedValue} matches expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
|
|
4719
|
+
s.logger.log(`OK to replace: computed=${computedValue} matches expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
|
|
4297
4720
|
return true;
|
|
4298
4721
|
}
|
|
4299
|
-
|
|
4300
|
-
logger$5.log(`Skip replace: computed=${computedValue} differs from expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px) - keeping current value`);
|
|
4722
|
+
s.logger.log(`Skip replace: computed=${computedValue} differs from expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
|
|
4301
4723
|
return false;
|
|
4302
4724
|
}
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
*/
|
|
4306
|
-
function applyPropertyWithVerification(element, prop, computedValue, originalValue, tolerance = DEFAULT_TOLERANCE_PX) {
|
|
4307
|
-
// Verify before replacing - only replace if computed value matches target config
|
|
4308
|
-
if (originalValue && shouldReplaceValue(computedValue, originalValue, tolerance)) {
|
|
4725
|
+
function applyPropertyWithVerification(s, element, prop, computedValue, originalValue, tolerance = DEFAULT_TOLERANCE_PX) {
|
|
4726
|
+
if (originalValue && shouldReplaceValue(s, computedValue, originalValue, tolerance)) {
|
|
4309
4727
|
element.style.setProperty(prop, computedValue, 'important');
|
|
4310
4728
|
return true;
|
|
4311
4729
|
}
|
|
4312
4730
|
else if (!originalValue) {
|
|
4313
|
-
// No original value tracked, use old behavior
|
|
4314
4731
|
element.style.setProperty(prop, computedValue, 'important');
|
|
4315
4732
|
return true;
|
|
4316
4733
|
}
|
|
4317
4734
|
return false;
|
|
4318
4735
|
}
|
|
4319
|
-
//
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
* Start the computed style enforcer
|
|
4324
|
-
* Initialize with iframe document and config
|
|
4325
|
-
*/
|
|
4326
|
-
function start$4(d, w, cfg, options = {}) {
|
|
4327
|
-
if (running$3) {
|
|
4328
|
-
logger$5.warn('Enforcer is already running. Call stop() first.');
|
|
4736
|
+
// ── Exported module-level functions ───────────────────────────────────────────
|
|
4737
|
+
function start$3(s, d, w, cfg, options = {}) {
|
|
4738
|
+
if (s.running) {
|
|
4739
|
+
s.logger.warn('Enforcer is already running. Call stop() first.');
|
|
4329
4740
|
return;
|
|
4330
4741
|
}
|
|
4331
|
-
doc
|
|
4332
|
-
win
|
|
4333
|
-
config
|
|
4334
|
-
running
|
|
4335
|
-
logger
|
|
4336
|
-
logger
|
|
4742
|
+
s.doc = d;
|
|
4743
|
+
s.win = w;
|
|
4744
|
+
s.config = cfg;
|
|
4745
|
+
s.running = true;
|
|
4746
|
+
s.logger.configure({ enabled: !!options.debug });
|
|
4747
|
+
s.logger.log('Computed style enforcer started');
|
|
4337
4748
|
}
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4749
|
+
function stop$3(s) {
|
|
4750
|
+
if (!s.running)
|
|
4751
|
+
return;
|
|
4752
|
+
s.doc = null;
|
|
4753
|
+
s.win = null;
|
|
4754
|
+
s.config = null;
|
|
4755
|
+
s.elementsWithViewportUnits.clear();
|
|
4756
|
+
s.originalValues = new WeakMap();
|
|
4757
|
+
s.running = false;
|
|
4758
|
+
s.logger.log('Computed style enforcer stopped');
|
|
4759
|
+
}
|
|
4760
|
+
function reset(s) {
|
|
4761
|
+
if (!s.running) {
|
|
4762
|
+
s.logger.warn('Enforcer is not running. Call start() first.');
|
|
4343
4763
|
return;
|
|
4344
4764
|
}
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
elementsWithViewportUnits$1.clear();
|
|
4349
|
-
originalValues$1 = new WeakMap();
|
|
4350
|
-
running$3 = false;
|
|
4351
|
-
logger$5.log('Computed style enforcer stopped');
|
|
4765
|
+
s.elementsWithViewportUnits.clear();
|
|
4766
|
+
s.originalValues = new WeakMap();
|
|
4767
|
+
s.logger.log('Computed style enforcer reset');
|
|
4352
4768
|
}
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
*/
|
|
4357
|
-
function trackElement(element, propertyOriginalValues) {
|
|
4358
|
-
if (!running$3) {
|
|
4359
|
-
logger$5.warn('Enforcer is not running. Call start() first.');
|
|
4769
|
+
function trackElement(s, element, propertyOriginalValues) {
|
|
4770
|
+
if (!s.running) {
|
|
4771
|
+
s.logger.warn('Enforcer is not running. Call start() first.');
|
|
4360
4772
|
return;
|
|
4361
4773
|
}
|
|
4362
|
-
elementsWithViewportUnits
|
|
4363
|
-
|
|
4364
|
-
let elementOriginals = originalValues$1.get(element);
|
|
4774
|
+
s.elementsWithViewportUnits.add(element);
|
|
4775
|
+
let elementOriginals = s.originalValues.get(element);
|
|
4365
4776
|
if (!elementOriginals) {
|
|
4366
4777
|
elementOriginals = new Map();
|
|
4367
|
-
originalValues
|
|
4778
|
+
s.originalValues.set(element, elementOriginals);
|
|
4368
4779
|
}
|
|
4369
|
-
// Merge property original values (don't override existing)
|
|
4370
4780
|
propertyOriginalValues.forEach((value, prop) => {
|
|
4371
|
-
if (!elementOriginals.has(prop))
|
|
4781
|
+
if (!elementOriginals.has(prop))
|
|
4372
4782
|
elementOriginals.set(prop, value);
|
|
4373
|
-
}
|
|
4374
4783
|
});
|
|
4375
4784
|
}
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
function processElement(element, options = {}) {
|
|
4380
|
-
if (!running$3 || !doc$1 || !win$1 || !config$2) {
|
|
4381
|
-
logger$5.warn('Enforcer is not running. Call start() first.');
|
|
4785
|
+
function processElement(s, element, options = {}) {
|
|
4786
|
+
if (!s.running || !s.doc || !s.win || !s.config) {
|
|
4787
|
+
s.logger.warn('Enforcer is not running.');
|
|
4382
4788
|
return 0;
|
|
4383
4789
|
}
|
|
4384
|
-
if (!elementsWithViewportUnits
|
|
4790
|
+
if (!s.elementsWithViewportUnits.has(element))
|
|
4385
4791
|
return 0;
|
|
4386
|
-
}
|
|
4387
4792
|
const htmlElement = element;
|
|
4388
|
-
const computed = win
|
|
4793
|
+
const computed = s.win.getComputedStyle(htmlElement);
|
|
4389
4794
|
const inlineStyle = htmlElement.style;
|
|
4390
|
-
const elementOriginals = originalValues
|
|
4795
|
+
const elementOriginals = s.originalValues.get(element);
|
|
4391
4796
|
const tolerance = options.tolerance ?? DEFAULT_TOLERANCE_PX;
|
|
4392
4797
|
let count = 0;
|
|
4393
4798
|
CRITICAL_PROPERTIES.forEach((prop) => {
|
|
4394
4799
|
const computedValue = computed.getPropertyValue(prop);
|
|
4395
4800
|
const inlineValue = inlineStyle.getPropertyValue(prop);
|
|
4396
|
-
if (computedValue && (!inlineValue || inlineValue !== computedValue)) {
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
count++;
|
|
4401
|
-
}
|
|
4402
|
-
}
|
|
4801
|
+
if (computedValue && (!inlineValue || inlineValue !== computedValue) && !isDefaultCssValue(computedValue)) {
|
|
4802
|
+
const originalValue = elementOriginals?.get(prop) || '';
|
|
4803
|
+
if (applyPropertyWithVerification(s, htmlElement, prop, computedValue, originalValue, tolerance))
|
|
4804
|
+
count++;
|
|
4403
4805
|
}
|
|
4404
4806
|
});
|
|
4405
4807
|
return count;
|
|
4406
4808
|
}
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
*/
|
|
4411
|
-
function processAll(options = {}) {
|
|
4412
|
-
if (!running$3 || !doc$1 || !win$1 || !config$2) {
|
|
4413
|
-
logger$5.warn('Enforcer is not running. Call start() first.');
|
|
4809
|
+
function processAll(s, options = {}) {
|
|
4810
|
+
if (!s.running || !s.doc || !s.win || !s.config) {
|
|
4811
|
+
s.logger.warn('Enforcer is not running.');
|
|
4414
4812
|
return 0;
|
|
4415
4813
|
}
|
|
4416
4814
|
let totalCount = 0;
|
|
4417
|
-
elementsWithViewportUnits
|
|
4418
|
-
totalCount += processElement(element, options);
|
|
4815
|
+
s.elementsWithViewportUnits.forEach((element) => {
|
|
4816
|
+
totalCount += processElement(s, element, options);
|
|
4419
4817
|
});
|
|
4420
|
-
logger
|
|
4818
|
+
s.logger.log(`Enforced ${totalCount} computed styles for ${s.elementsWithViewportUnits.size} elements`);
|
|
4421
4819
|
return totalCount;
|
|
4422
4820
|
}
|
|
4821
|
+
function updateConfig$2(s, cfg) {
|
|
4822
|
+
if (!s.running || !s.config) {
|
|
4823
|
+
s.logger.warn('Enforcer is not running.');
|
|
4824
|
+
return;
|
|
4825
|
+
}
|
|
4826
|
+
s.config = { ...s.config, ...cfg };
|
|
4827
|
+
s.logger.log('Config updated:', cfg);
|
|
4828
|
+
}
|
|
4829
|
+
|
|
4830
|
+
/**
|
|
4831
|
+
* Computed Style Enforcer
|
|
4832
|
+
* Enforces computed CSS styles with viewport unit verification.
|
|
4833
|
+
*/
|
|
4834
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
4835
|
+
function createEnforcer() {
|
|
4836
|
+
const s = {
|
|
4837
|
+
logger: createLogger({ enabled: false, prefix: 'ComputedStyleEnforcer' }),
|
|
4838
|
+
doc: null,
|
|
4839
|
+
win: null,
|
|
4840
|
+
config: null,
|
|
4841
|
+
elementsWithViewportUnits: new Set(),
|
|
4842
|
+
originalValues: new WeakMap(),
|
|
4843
|
+
running: false,
|
|
4844
|
+
};
|
|
4845
|
+
return {
|
|
4846
|
+
start: (d, w, cfg, options) => start$3(s, d, w, cfg, options),
|
|
4847
|
+
stop: () => stop$3(s),
|
|
4848
|
+
reset: () => reset(s),
|
|
4849
|
+
trackElement: (element, propertyOriginalValues) => trackElement(s, element, propertyOriginalValues),
|
|
4850
|
+
processElement: (element, options) => processElement(s, element, options),
|
|
4851
|
+
processAll: (options) => processAll(s, options),
|
|
4852
|
+
updateConfig: (cfg) => updateConfig$2(s, cfg),
|
|
4853
|
+
getStateInfo: () => ({
|
|
4854
|
+
isRunning: s.running,
|
|
4855
|
+
trackedElementsCount: s.elementsWithViewportUnits.size,
|
|
4856
|
+
hasConfig: !!s.config,
|
|
4857
|
+
}),
|
|
4858
|
+
isRunning: () => s.running,
|
|
4859
|
+
};
|
|
4860
|
+
}
|
|
4861
|
+
|
|
4862
|
+
const registry$1 = [];
|
|
4863
|
+
/**
|
|
4864
|
+
* Register a global fix.
|
|
4865
|
+
* Fixes are run in registration order.
|
|
4866
|
+
*/
|
|
4867
|
+
function register$1(fix) {
|
|
4868
|
+
registry$1.push(fix);
|
|
4869
|
+
}
|
|
4870
|
+
/**
|
|
4871
|
+
* Returns all fixes that are active for the given context.
|
|
4872
|
+
* A fix is active if it has no `shouldApply`, or `shouldApply` returns true.
|
|
4873
|
+
*/
|
|
4874
|
+
function getActiveFixes(ctx) {
|
|
4875
|
+
return registry$1.filter((fix) => {
|
|
4876
|
+
try {
|
|
4877
|
+
return !fix.shouldApply || fix.shouldApply(ctx);
|
|
4878
|
+
}
|
|
4879
|
+
catch {
|
|
4880
|
+
return false;
|
|
4881
|
+
}
|
|
4882
|
+
});
|
|
4883
|
+
}
|
|
4423
4884
|
|
|
4424
4885
|
/**
|
|
4425
4886
|
* Core viewport unit replacement logic.
|
|
4426
4887
|
* Converts vh/vw/svh/dvh/% to pixel values across all CSS in the iframe.
|
|
4427
4888
|
*/
|
|
4428
|
-
const logger$
|
|
4889
|
+
const logger$1 = createLogger({ enabled: false, prefix: 'ViewportUnitReplacer' });
|
|
4429
4890
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
4430
4891
|
const HEIGHT_RELATED_PROPERTIES = ['height', 'min-height', 'max-height', 'top', 'bottom'];
|
|
4892
|
+
/**
|
|
4893
|
+
* Number of top-level CSS rules to process before yielding to the browser.
|
|
4894
|
+
* Keeps the main thread responsive during large stylesheets (prevents tab kill on mobile).
|
|
4895
|
+
*/
|
|
4896
|
+
const YIELD_EVERY_RULES = 100;
|
|
4897
|
+
// ─── Scheduler ────────────────────────────────────────────────────────────────
|
|
4898
|
+
/**
|
|
4899
|
+
* Yield control back to the browser so it can handle input, paint frames, and
|
|
4900
|
+
* avoid "page unresponsive" / tab-kill on mobile during heavy CSS processing.
|
|
4901
|
+
*
|
|
4902
|
+
* Uses `scheduler.yield()` (Chrome 115+) when available; falls back to a
|
|
4903
|
+
* zero-timeout macrotask which is universally supported.
|
|
4904
|
+
*/
|
|
4905
|
+
function yieldToMain() {
|
|
4906
|
+
if (typeof globalThis !== 'undefined' && 'scheduler' in globalThis) {
|
|
4907
|
+
return globalThis.scheduler.yield();
|
|
4908
|
+
}
|
|
4909
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
4910
|
+
}
|
|
4431
4911
|
// ─── Per-run tracking state (reset on each process() call) ───────────────────
|
|
4432
4912
|
let elementsWithViewportUnits = new Set();
|
|
4433
4913
|
let originalValues = new WeakMap();
|
|
4434
4914
|
// ─── Regex ────────────────────────────────────────────────────────────────────
|
|
4435
|
-
/**
|
|
4915
|
+
/**
|
|
4916
|
+
* Stateless test-only regex (no `g` flag) — safe to share across calls.
|
|
4917
|
+
* Used exclusively for `.test()` checks before doing a full replacement.
|
|
4918
|
+
*/
|
|
4919
|
+
const VIEWPORT_RE_TEST = /([-.?\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/i;
|
|
4920
|
+
/** Fresh `g`-flagged instance for String.replace() callbacks. */
|
|
4436
4921
|
function createRegex() {
|
|
4437
|
-
return /([
|
|
4922
|
+
return /([-.?\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/gi;
|
|
4438
4923
|
}
|
|
4439
4924
|
// ─── Unit conversion ─────────────────────────────────────────────────────────
|
|
4440
4925
|
function px(value) {
|
|
4441
4926
|
return `${value.toFixed(2)}px`;
|
|
4442
4927
|
}
|
|
4443
|
-
function
|
|
4928
|
+
function buildUnitMap(ctx) {
|
|
4444
4929
|
return {
|
|
4445
4930
|
vh: ctx.targetHeight,
|
|
4446
4931
|
svh: ctx.targetHeight,
|
|
@@ -4452,15 +4937,15 @@ function getUnitMap(ctx) {
|
|
|
4452
4937
|
dvw: ctx.targetWidth,
|
|
4453
4938
|
};
|
|
4454
4939
|
}
|
|
4455
|
-
function toPx(value, unit,
|
|
4940
|
+
function toPx(value, unit, unitMap, targetHeight) {
|
|
4456
4941
|
const u = unit.toLowerCase();
|
|
4457
4942
|
if (u === '%')
|
|
4458
|
-
return (value / 100) *
|
|
4459
|
-
return (value / 100) * (
|
|
4943
|
+
return (value / 100) * targetHeight;
|
|
4944
|
+
return (value / 100) * (unitMap[u] ?? 0);
|
|
4460
4945
|
}
|
|
4461
|
-
function convert(value, unit,
|
|
4946
|
+
function convert(value, unit, unitMap, targetHeight) {
|
|
4462
4947
|
const num = parseFloat(value);
|
|
4463
|
-
return isNaN(num) ? value : px(toPx(num, unit,
|
|
4948
|
+
return isNaN(num) ? value : px(toPx(num, unit, unitMap, targetHeight));
|
|
4464
4949
|
}
|
|
4465
4950
|
function isHeightRelated(prop) {
|
|
4466
4951
|
return HEIGHT_RELATED_PROPERTIES.includes(prop);
|
|
@@ -4475,11 +4960,13 @@ function extractProperty(cssText, matchOffset) {
|
|
|
4475
4960
|
return m ? m[1].toLowerCase() : '';
|
|
4476
4961
|
}
|
|
4477
4962
|
function replaceInText(cssText, ctx) {
|
|
4963
|
+
const unitMap = buildUnitMap(ctx);
|
|
4964
|
+
const { targetHeight } = ctx;
|
|
4478
4965
|
return cssText.replace(createRegex(), (match, value, unit, offset) => {
|
|
4479
4966
|
if (unit === '%') {
|
|
4480
|
-
return isHeightRelated(extractProperty(cssText, offset)) ? convert(value, unit,
|
|
4967
|
+
return isHeightRelated(extractProperty(cssText, offset)) ? convert(value, unit, unitMap, targetHeight) : match;
|
|
4481
4968
|
}
|
|
4482
|
-
return convert(value, unit,
|
|
4969
|
+
return convert(value, unit, unitMap, targetHeight);
|
|
4483
4970
|
});
|
|
4484
4971
|
}
|
|
4485
4972
|
// ─── Element tracking ─────────────────────────────────────────────────────────
|
|
@@ -4496,11 +4983,11 @@ function trackSelector(selector, propOriginals, ctx) {
|
|
|
4496
4983
|
if (!originals.has(k))
|
|
4497
4984
|
originals.set(k, v);
|
|
4498
4985
|
});
|
|
4499
|
-
trackElement(el, propOriginals);
|
|
4986
|
+
ctx.enforcer?.trackElement(el, propOriginals);
|
|
4500
4987
|
});
|
|
4501
4988
|
}
|
|
4502
4989
|
catch {
|
|
4503
|
-
logger$
|
|
4990
|
+
logger$1.warn('Invalid selector, skipping:', selector);
|
|
4504
4991
|
}
|
|
4505
4992
|
}
|
|
4506
4993
|
// ─── CSS processing ───────────────────────────────────────────────────────────
|
|
@@ -4508,25 +4995,25 @@ function processInlineStyles(ctx) {
|
|
|
4508
4995
|
let count = 0;
|
|
4509
4996
|
ctx.doc.querySelectorAll('[style]').forEach((el) => {
|
|
4510
4997
|
const style = el.getAttribute('style');
|
|
4511
|
-
if (style &&
|
|
4998
|
+
if (style && VIEWPORT_RE_TEST.test(style)) {
|
|
4512
4999
|
elementsWithViewportUnits.add(el);
|
|
4513
5000
|
el.setAttribute('style', replaceInText(style, ctx));
|
|
4514
5001
|
count++;
|
|
4515
5002
|
}
|
|
4516
5003
|
});
|
|
4517
|
-
logger$
|
|
5004
|
+
logger$1.log(`Replaced ${count} inline style elements`);
|
|
4518
5005
|
return count;
|
|
4519
5006
|
}
|
|
4520
5007
|
function processStyleTags(ctx) {
|
|
4521
5008
|
let count = 0;
|
|
4522
5009
|
ctx.doc.querySelectorAll('style').forEach((tag) => {
|
|
4523
5010
|
const css = tag.textContent || '';
|
|
4524
|
-
if (
|
|
5011
|
+
if (VIEWPORT_RE_TEST.test(css)) {
|
|
4525
5012
|
tag.textContent = replaceInText(css, ctx);
|
|
4526
5013
|
count++;
|
|
4527
5014
|
}
|
|
4528
5015
|
});
|
|
4529
|
-
logger$
|
|
5016
|
+
logger$1.log(`Replaced ${count} <style> tags`);
|
|
4530
5017
|
return count;
|
|
4531
5018
|
}
|
|
4532
5019
|
function processRule(rule, ctx) {
|
|
@@ -4539,7 +5026,7 @@ function processRule(rule, ctx) {
|
|
|
4539
5026
|
for (let i = 0; i < style.length; i++) {
|
|
4540
5027
|
const prop = style[i];
|
|
4541
5028
|
const value = style.getPropertyValue(prop);
|
|
4542
|
-
if (value &&
|
|
5029
|
+
if (value && VIEWPORT_RE_TEST.test(value)) {
|
|
4543
5030
|
hasVp = true;
|
|
4544
5031
|
propOriginals.set(prop, value);
|
|
4545
5032
|
style.setProperty(prop, replaceInText(value, ctx), style.getPropertyPriority(prop));
|
|
@@ -4550,43 +5037,56 @@ function processRule(rule, ctx) {
|
|
|
4550
5037
|
trackSelector(cssRule.selectorText, propOriginals, ctx);
|
|
4551
5038
|
}
|
|
4552
5039
|
if ('cssRules' in rule) {
|
|
4553
|
-
|
|
4554
|
-
|
|
5040
|
+
const nested = rule.cssRules;
|
|
5041
|
+
if (nested) {
|
|
5042
|
+
for (let i = 0; i < nested.length; i++) {
|
|
5043
|
+
count += processRule(nested[i], ctx);
|
|
5044
|
+
}
|
|
4555
5045
|
}
|
|
4556
5046
|
}
|
|
4557
5047
|
return count;
|
|
4558
5048
|
}
|
|
4559
5049
|
/** Processes only inline <style> sheets. Linked sheets are handled by processLinkedStylesheets. */
|
|
4560
|
-
function processStylesheets(ctx) {
|
|
5050
|
+
async function processStylesheets(ctx) {
|
|
4561
5051
|
let total = 0;
|
|
4562
|
-
|
|
5052
|
+
let rulesSinceYield = 0;
|
|
5053
|
+
const sheets = ctx.doc.styleSheets;
|
|
5054
|
+
for (let i = 0; i < sheets.length; i++) {
|
|
5055
|
+
const sheet = sheets[i];
|
|
4563
5056
|
if (sheet.href)
|
|
4564
|
-
|
|
5057
|
+
continue; // deferred to processLinkedStylesheets
|
|
4565
5058
|
try {
|
|
4566
|
-
|
|
4567
|
-
|
|
5059
|
+
const rules = sheet.cssRules;
|
|
5060
|
+
for (let j = 0; j < rules.length; j++) {
|
|
5061
|
+
total += processRule(rules[j], ctx);
|
|
5062
|
+
rulesSinceYield++;
|
|
5063
|
+
if (rulesSinceYield >= YIELD_EVERY_RULES) {
|
|
5064
|
+
rulesSinceYield = 0;
|
|
5065
|
+
await yieldToMain();
|
|
5066
|
+
}
|
|
4568
5067
|
}
|
|
4569
5068
|
}
|
|
4570
5069
|
catch (e) {
|
|
4571
|
-
logger$
|
|
5070
|
+
logger$1.warn('Cannot read stylesheet (CORS?):', e.message);
|
|
4572
5071
|
}
|
|
4573
|
-
}
|
|
4574
|
-
logger$
|
|
5072
|
+
}
|
|
5073
|
+
logger$1.log(`Replaced ${total} rules in inline stylesheets`);
|
|
4575
5074
|
return total;
|
|
4576
5075
|
}
|
|
4577
5076
|
async function processLinkedStylesheets(ctx) {
|
|
4578
5077
|
const links = ctx.doc.querySelectorAll('link[rel="stylesheet"]');
|
|
4579
5078
|
let count = 0;
|
|
4580
|
-
for (
|
|
5079
|
+
for (let i = 0; i < links.length; i++) {
|
|
5080
|
+
const link = links[i];
|
|
4581
5081
|
// Skip cross-origin — already in browser CSSOM, handled via processStylesheets
|
|
4582
5082
|
if (link.href && !link.href.startsWith(ctx.win.location.origin)) {
|
|
4583
|
-
logger$
|
|
5083
|
+
logger$1.log('Skipping cross-origin CSS:', link.href);
|
|
4584
5084
|
continue;
|
|
4585
5085
|
}
|
|
4586
5086
|
try {
|
|
4587
5087
|
const res = await fetch(link.href);
|
|
4588
5088
|
let css = await res.text();
|
|
4589
|
-
if (
|
|
5089
|
+
if (VIEWPORT_RE_TEST.test(css)) {
|
|
4590
5090
|
css = replaceInText(css, ctx);
|
|
4591
5091
|
const style = ctx.doc.createElement('style');
|
|
4592
5092
|
style.textContent = css;
|
|
@@ -4597,27 +5097,27 @@ async function processLinkedStylesheets(ctx) {
|
|
|
4597
5097
|
}
|
|
4598
5098
|
}
|
|
4599
5099
|
catch (e) {
|
|
4600
|
-
logger$
|
|
5100
|
+
logger$1.warn('Cannot load CSS:', link.href, e);
|
|
4601
5101
|
}
|
|
4602
5102
|
}
|
|
4603
|
-
logger$
|
|
5103
|
+
logger$1.log(`Replaced ${count} linked CSS files`);
|
|
4604
5104
|
return count;
|
|
4605
5105
|
}
|
|
4606
5106
|
// ─── Public entry point ───────────────────────────────────────────────────────
|
|
4607
5107
|
async function process$1(ctx) {
|
|
4608
|
-
logger$
|
|
5108
|
+
logger$1.configure({ enabled: !!ctx.debug });
|
|
4609
5109
|
// Reset tracking state from any previous run
|
|
4610
5110
|
elementsWithViewportUnits = new Set();
|
|
4611
5111
|
originalValues = new WeakMap();
|
|
4612
5112
|
processInlineStyles(ctx);
|
|
4613
5113
|
processStyleTags(ctx);
|
|
4614
|
-
processStylesheets(ctx);
|
|
5114
|
+
await processStylesheets(ctx);
|
|
4615
5115
|
await processLinkedStylesheets(ctx);
|
|
4616
5116
|
// Wait for browser to apply the replaced styles
|
|
4617
5117
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
4618
5118
|
// Enforce final computed styles to inline with !important
|
|
4619
|
-
const count = processAll({ debug: !!ctx.debug });
|
|
4620
|
-
logger$
|
|
5119
|
+
const count = ctx.enforcer?.processAll({ debug: !!ctx.debug }) ?? 0;
|
|
5120
|
+
logger$1.log(`Enforced ${count} computed styles for ${elementsWithViewportUnits.size} tracked elements`);
|
|
4621
5121
|
}
|
|
4622
5122
|
|
|
4623
5123
|
/**
|
|
@@ -4713,6 +5213,9 @@ function getMaxWByDeviceType(deviceType) {
|
|
|
4713
5213
|
// ─── Main fix ─────────────────────────────────────────────────────────────────
|
|
4714
5214
|
function afterProcess$1({ doc, targetWidth, deviceType }) {
|
|
4715
5215
|
doc.querySelectorAll(SLIDER_ITEM_SELECTOR).forEach((item) => {
|
|
5216
|
+
// When !gp-min-w-full is set, the item fills 100% width via Tailwind — skip manual width override
|
|
5217
|
+
if (item.classList.contains('!gp-min-w-full'))
|
|
5218
|
+
return;
|
|
4716
5219
|
const originalWidth = parseFloat(item.style.minWidth) || parseFloat(item.style.maxWidth) || 0;
|
|
4717
5220
|
if (!originalWidth)
|
|
4718
5221
|
return;
|
|
@@ -4813,23 +5316,6 @@ register$1({
|
|
|
4813
5316
|
afterProcess,
|
|
4814
5317
|
});
|
|
4815
5318
|
|
|
4816
|
-
const logger$3 = createLogger({ enabled: false, prefix: 'ViewportReplacer' });
|
|
4817
|
-
let dimensionsListener = null;
|
|
4818
|
-
function attach(debug) {
|
|
4819
|
-
logger$3.configure({ enabled: !!debug });
|
|
4820
|
-
dimensionsListener = (e) => {
|
|
4821
|
-
const ev = e;
|
|
4822
|
-
logger$3.log('Dimensions applied:', ev.detail);
|
|
4823
|
-
};
|
|
4824
|
-
window.addEventListener('iframe-dimensions-applied', dimensionsListener);
|
|
4825
|
-
}
|
|
4826
|
-
function detach() {
|
|
4827
|
-
if (dimensionsListener) {
|
|
4828
|
-
window.removeEventListener('iframe-dimensions-applied', dimensionsListener);
|
|
4829
|
-
dimensionsListener = null;
|
|
4830
|
-
}
|
|
4831
|
-
}
|
|
4832
|
-
|
|
4833
5319
|
/**
|
|
4834
5320
|
* Default iframe dimension calculation.
|
|
4835
5321
|
* Used as fallback when no fix overrides getDimensions().
|
|
@@ -4853,41 +5339,58 @@ function getFinalWidth(doc) {
|
|
|
4853
5339
|
* Phase 3: afterProcess (shop → global)
|
|
4854
5340
|
* Phase 4: getDimensions (shop → global → default)
|
|
4855
5341
|
*/
|
|
4856
|
-
const logger
|
|
5342
|
+
const logger = createLogger({ enabled: false, prefix: 'ViewportReplacer' });
|
|
4857
5343
|
function configure(debug) {
|
|
4858
|
-
logger
|
|
5344
|
+
logger.configure({ enabled: debug });
|
|
4859
5345
|
}
|
|
4860
5346
|
async function run$1(ctx, activeGlobal, shopFix) {
|
|
4861
5347
|
// ── Phase 1: beforeProcess ────────────────────────────────────────────────
|
|
5348
|
+
const t1 = perf.mark('phase1.beforeProcess');
|
|
4862
5349
|
for (const fix of activeGlobal) {
|
|
4863
5350
|
if (fix.beforeProcess) {
|
|
4864
|
-
logger
|
|
5351
|
+
logger.log(`[beforeProcess] ${fix.name}`);
|
|
5352
|
+
const t = perf.mark(`phase1.${fix.name}.beforeProcess`);
|
|
4865
5353
|
await fix.beforeProcess(ctx);
|
|
5354
|
+
perf.measure(`phase1.${fix.name}.beforeProcess`, t);
|
|
4866
5355
|
}
|
|
4867
5356
|
}
|
|
4868
5357
|
if (shopFix?.beforeProcess) {
|
|
4869
|
-
logger
|
|
5358
|
+
logger.log('[beforeProcess] shop');
|
|
5359
|
+
const t = perf.mark('phase1.shop.beforeProcess');
|
|
4870
5360
|
await shopFix.beforeProcess(ctx);
|
|
5361
|
+
perf.measure('phase1.shop.beforeProcess', t);
|
|
4871
5362
|
}
|
|
5363
|
+
perf.measure('phase1.beforeProcess', t1);
|
|
4872
5364
|
// ── Phase 2: process ──────────────────────────────────────────────────────
|
|
5365
|
+
const t2 = perf.mark('phase2.process');
|
|
4873
5366
|
for (const fix of activeGlobal) {
|
|
4874
5367
|
if (fix.process) {
|
|
4875
|
-
logger
|
|
5368
|
+
logger.log(`[process] ${fix.name}`);
|
|
5369
|
+
const t = perf.mark(`phase2.${fix.name}.process`);
|
|
4876
5370
|
await fix.process(ctx);
|
|
5371
|
+
perf.measure(`phase2.${fix.name}.process`, t);
|
|
4877
5372
|
}
|
|
4878
5373
|
}
|
|
5374
|
+
perf.measure('phase2.process', t2);
|
|
4879
5375
|
// ── Phase 3: afterProcess ─────────────────────────────────────────────────
|
|
5376
|
+
const t3 = perf.mark('phase3.afterProcess');
|
|
4880
5377
|
if (shopFix?.afterProcess) {
|
|
4881
|
-
logger
|
|
5378
|
+
logger.log('[afterProcess] shop');
|
|
5379
|
+
const t = perf.mark('phase3.shop.afterProcess');
|
|
4882
5380
|
await shopFix.afterProcess(ctx);
|
|
5381
|
+
perf.measure('phase3.shop.afterProcess', t);
|
|
4883
5382
|
}
|
|
4884
5383
|
for (const fix of activeGlobal) {
|
|
4885
5384
|
if (fix.afterProcess) {
|
|
4886
|
-
logger
|
|
5385
|
+
logger.log(`[afterProcess] ${fix.name}`);
|
|
5386
|
+
const t = perf.mark(`phase3.${fix.name}.afterProcess`);
|
|
4887
5387
|
await fix.afterProcess(ctx);
|
|
5388
|
+
perf.measure(`phase3.${fix.name}.afterProcess`, t);
|
|
4888
5389
|
}
|
|
4889
5390
|
}
|
|
5391
|
+
perf.measure('phase3.afterProcess', t3);
|
|
4890
5392
|
// ── Phase 4: getDimensions ────────────────────────────────────────────────
|
|
5393
|
+
const t4 = perf.mark('phase4.getDimensions');
|
|
4891
5394
|
return new Promise((resolve) => {
|
|
4892
5395
|
requestAnimationFrame(() => {
|
|
4893
5396
|
let dimensions = null;
|
|
@@ -4895,14 +5398,14 @@ async function run$1(ctx, activeGlobal, shopFix) {
|
|
|
4895
5398
|
if (shopFix?.getDimensions) {
|
|
4896
5399
|
dimensions = shopFix.getDimensions(ctx);
|
|
4897
5400
|
if (dimensions)
|
|
4898
|
-
logger
|
|
5401
|
+
logger.log('Dimensions from shop fix:', dimensions);
|
|
4899
5402
|
}
|
|
4900
5403
|
if (!dimensions) {
|
|
4901
5404
|
for (const fix of activeGlobal) {
|
|
4902
5405
|
if (fix.getDimensions) {
|
|
4903
5406
|
dimensions = fix.getDimensions(ctx);
|
|
4904
5407
|
if (dimensions) {
|
|
4905
|
-
logger
|
|
5408
|
+
logger.log(`Dimensions from global fix [${fix.name}]:`, dimensions);
|
|
4906
5409
|
break;
|
|
4907
5410
|
}
|
|
4908
5411
|
}
|
|
@@ -4911,7 +5414,8 @@ async function run$1(ctx, activeGlobal, shopFix) {
|
|
|
4911
5414
|
if (!dimensions) {
|
|
4912
5415
|
dimensions = { height: getFinalHeight(ctx.doc, ctx.win), width: getFinalWidth(ctx.doc) };
|
|
4913
5416
|
}
|
|
4914
|
-
logger
|
|
5417
|
+
logger.log('Final dimensions:', dimensions);
|
|
5418
|
+
perf.measure('phase4.getDimensions', t4);
|
|
4915
5419
|
resolve(dimensions);
|
|
4916
5420
|
});
|
|
4917
5421
|
});
|
|
@@ -4988,232 +5492,286 @@ register(`566240210141053597`, {
|
|
|
4988
5492
|
afterProcess: restoreAndFixLayout,
|
|
4989
5493
|
});
|
|
4990
5494
|
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
let config$1 = null;
|
|
4996
|
-
let shopFix = null;
|
|
4997
|
-
let running$2 = false;
|
|
4998
|
-
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
4999
|
-
function start$3(iframe, cfg) {
|
|
5000
|
-
if (running$2) {
|
|
5001
|
-
logger$1.warn('Already running. Call stop() first.');
|
|
5495
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
5496
|
+
function start$2(s, iframe, cfg) {
|
|
5497
|
+
if (s.running) {
|
|
5498
|
+
s.logger.warn('Already running. Call stop() first.');
|
|
5002
5499
|
return;
|
|
5003
5500
|
}
|
|
5004
5501
|
if (!iframe.contentDocument || !iframe.contentWindow) {
|
|
5005
5502
|
throw new Error('Iframe document or window not accessible');
|
|
5006
5503
|
}
|
|
5007
|
-
doc = iframe.contentDocument;
|
|
5008
|
-
win = iframe.contentWindow;
|
|
5009
|
-
config
|
|
5010
|
-
running
|
|
5011
|
-
shopFix = cfg.shopId != null ? get(cfg.shopId) : null;
|
|
5012
|
-
if (shopFix)
|
|
5013
|
-
logger
|
|
5014
|
-
}
|
|
5015
|
-
logger$1.configure({ enabled: !!cfg.debug });
|
|
5504
|
+
s.doc = iframe.contentDocument;
|
|
5505
|
+
s.win = iframe.contentWindow;
|
|
5506
|
+
s.config = cfg;
|
|
5507
|
+
s.running = true;
|
|
5508
|
+
s.shopFix = cfg.shopId != null ? get(cfg.shopId) : null;
|
|
5509
|
+
if (s.shopFix)
|
|
5510
|
+
s.logger.log(`Shop fix loaded for "${cfg.shopId}":`, s.shopFix.description ?? '(no description)');
|
|
5511
|
+
s.logger.configure({ enabled: !!cfg.debug });
|
|
5016
5512
|
configure(!!cfg.debug);
|
|
5017
|
-
start
|
|
5018
|
-
attach(cfg.debug);
|
|
5019
|
-
logger
|
|
5513
|
+
s.enforcer.start(s.doc, s.win, cfg, { debug: !!cfg.debug });
|
|
5514
|
+
s.listeners.attach(cfg.debug);
|
|
5515
|
+
s.logger.log('Started');
|
|
5020
5516
|
}
|
|
5021
|
-
function stop$
|
|
5022
|
-
if (!running
|
|
5517
|
+
function stop$2(s) {
|
|
5518
|
+
if (!s.running)
|
|
5023
5519
|
return;
|
|
5024
|
-
stop
|
|
5025
|
-
detach();
|
|
5026
|
-
doc = null;
|
|
5027
|
-
win = null;
|
|
5028
|
-
config
|
|
5029
|
-
shopFix = null;
|
|
5030
|
-
running
|
|
5031
|
-
logger
|
|
5032
|
-
}
|
|
5033
|
-
async function run() {
|
|
5034
|
-
if (!running
|
|
5035
|
-
logger
|
|
5520
|
+
s.enforcer.stop();
|
|
5521
|
+
s.listeners.detach();
|
|
5522
|
+
s.doc = null;
|
|
5523
|
+
s.win = null;
|
|
5524
|
+
s.config = null;
|
|
5525
|
+
s.shopFix = null;
|
|
5526
|
+
s.running = false;
|
|
5527
|
+
s.logger.log('Stopped');
|
|
5528
|
+
}
|
|
5529
|
+
async function run(s) {
|
|
5530
|
+
if (!s.running || !s.doc || !s.win || !s.config) {
|
|
5531
|
+
s.logger.warn('Not running. Call start() first.');
|
|
5036
5532
|
return { height: 1000, width: 1000 };
|
|
5037
5533
|
}
|
|
5038
5534
|
const ctx = {
|
|
5039
|
-
doc,
|
|
5040
|
-
win,
|
|
5041
|
-
deviceType: config
|
|
5042
|
-
targetWidth: config
|
|
5043
|
-
targetHeight: config
|
|
5044
|
-
debug: config
|
|
5535
|
+
doc: s.doc,
|
|
5536
|
+
win: s.win,
|
|
5537
|
+
deviceType: s.config.deviceType,
|
|
5538
|
+
targetWidth: s.config.targetWidth,
|
|
5539
|
+
targetHeight: s.config.targetHeight,
|
|
5540
|
+
debug: s.config.debug,
|
|
5541
|
+
enforcer: s.enforcer,
|
|
5045
5542
|
};
|
|
5046
5543
|
const activeGlobal = getActiveFixes(ctx);
|
|
5047
|
-
if (activeGlobal.length > 0)
|
|
5048
|
-
logger
|
|
5049
|
-
|
|
5544
|
+
if (activeGlobal.length > 0)
|
|
5545
|
+
s.logger.log(`Active global fixes: ${activeGlobal.map((f) => f.name).join(', ')}`);
|
|
5546
|
+
const tRun = perf.mark('viewport.run');
|
|
5050
5547
|
try {
|
|
5051
|
-
|
|
5548
|
+
const result = await run$1(ctx, activeGlobal, s.shopFix);
|
|
5549
|
+
perf.measure('viewport.run', tRun);
|
|
5550
|
+
return result;
|
|
5052
5551
|
}
|
|
5053
5552
|
catch (err) {
|
|
5054
|
-
|
|
5055
|
-
|
|
5553
|
+
perf.measure('viewport.run', tRun);
|
|
5554
|
+
s.logger.error('Critical error:', err);
|
|
5555
|
+
return { height: s.doc.body?.scrollHeight || 1000, width: s.doc.body?.scrollWidth || 1000 };
|
|
5056
5556
|
}
|
|
5057
5557
|
}
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
prefix: 'IframeFixer',
|
|
5062
|
-
});
|
|
5063
|
-
// ============================================================================
|
|
5064
|
-
// State
|
|
5065
|
-
// ============================================================================
|
|
5066
|
-
let iframe = null;
|
|
5067
|
-
let config = null;
|
|
5068
|
-
let running$1 = false;
|
|
5069
|
-
let loadListener = null;
|
|
5070
|
-
// ============================================================================
|
|
5071
|
-
// Core API Functions
|
|
5072
|
-
// ============================================================================
|
|
5073
|
-
function start$2(cfg) {
|
|
5074
|
-
if (running$1) {
|
|
5075
|
-
logger.warn('Fixer is already running. Call stop() first.');
|
|
5558
|
+
function updateConfig$1(s, cfg) {
|
|
5559
|
+
if (!s.running || !s.config) {
|
|
5560
|
+
s.logger.warn('Not running. Call start() first.');
|
|
5076
5561
|
return;
|
|
5077
5562
|
}
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
logger.log('
|
|
5083
|
-
|
|
5563
|
+
s.config = { ...s.config, ...cfg };
|
|
5564
|
+
if (cfg.shopId !== undefined)
|
|
5565
|
+
s.shopFix = cfg.shopId != null ? get(cfg.shopId) : null;
|
|
5566
|
+
s.enforcer.updateConfig(cfg);
|
|
5567
|
+
s.logger.log('Config updated');
|
|
5568
|
+
}
|
|
5569
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
5570
|
+
function createViewportProcessor() {
|
|
5571
|
+
const s = {
|
|
5572
|
+
logger: createLogger({ enabled: false, prefix: 'ViewportReplacer' }),
|
|
5573
|
+
enforcer: createEnforcer(),
|
|
5574
|
+
listeners: createViewportListeners(),
|
|
5575
|
+
doc: null,
|
|
5576
|
+
win: null,
|
|
5577
|
+
config: null,
|
|
5578
|
+
shopFix: null,
|
|
5579
|
+
running: false,
|
|
5580
|
+
};
|
|
5581
|
+
return {
|
|
5582
|
+
start: (iframe, cfg) => start$2(s, iframe, cfg),
|
|
5583
|
+
stop: () => stop$2(s),
|
|
5584
|
+
run: () => run(s),
|
|
5585
|
+
updateConfig: (cfg) => updateConfig$1(s, cfg),
|
|
5586
|
+
isRunning: () => s.running,
|
|
5587
|
+
};
|
|
5588
|
+
}
|
|
5589
|
+
|
|
5590
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
5591
|
+
function dispatchDimensionsEvent(dimensions) {
|
|
5592
|
+
window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', { detail: dimensions }));
|
|
5084
5593
|
}
|
|
5085
|
-
function
|
|
5086
|
-
if (!
|
|
5594
|
+
async function process(s) {
|
|
5595
|
+
if (!s.iframe || !s.config)
|
|
5087
5596
|
return;
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
stop$5();
|
|
5092
|
-
// Remove load listener
|
|
5093
|
-
if (iframe && loadListener) {
|
|
5094
|
-
iframe.removeEventListener('load', loadListener);
|
|
5095
|
-
loadListener = null;
|
|
5096
|
-
}
|
|
5097
|
-
iframe = null;
|
|
5098
|
-
config = null;
|
|
5099
|
-
running$1 = false;
|
|
5100
|
-
logger.log('Iframe fixer stopped');
|
|
5101
|
-
}
|
|
5102
|
-
async function initialize() {
|
|
5103
|
-
if (!iframe || !config) {
|
|
5104
|
-
logger.error('iframe not found');
|
|
5105
|
-
config?.onError?.(new Error('iframe not found'));
|
|
5597
|
+
if (!s.iframe.contentDocument || !s.iframe.contentWindow) {
|
|
5598
|
+
s.logger.error('Cannot access iframe document');
|
|
5599
|
+
s.config.onError?.(new Error('Cannot access iframe document'));
|
|
5106
5600
|
return;
|
|
5107
5601
|
}
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5602
|
+
const sessionId = `render-${Date.now()}`;
|
|
5603
|
+
perf.startSession(sessionId);
|
|
5604
|
+
const t0 = perf.mark('orchestrator.process');
|
|
5605
|
+
try {
|
|
5606
|
+
s.logger.log('Processing viewport units...');
|
|
5607
|
+
s.viewportReplacer.start(s.iframe, s.config);
|
|
5608
|
+
s.navigationBlocker.start(s.iframe, { debug: s.config.debug });
|
|
5609
|
+
const result = await s.viewportReplacer.run();
|
|
5610
|
+
perf.measure('orchestrator.process', t0);
|
|
5611
|
+
perf.endSession();
|
|
5612
|
+
s.logger.log('Process completed:', result);
|
|
5613
|
+
s.config.onSuccess?.(result);
|
|
5614
|
+
dispatchDimensionsEvent(result);
|
|
5111
5615
|
}
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5616
|
+
catch (error) {
|
|
5617
|
+
perf.measure('orchestrator.process', t0);
|
|
5618
|
+
perf.endSession();
|
|
5619
|
+
s.logger.error('Failed to process:', error);
|
|
5620
|
+
s.config.onError?.(error);
|
|
5115
5621
|
}
|
|
5116
5622
|
}
|
|
5117
|
-
async function
|
|
5118
|
-
if (!iframe || !config)
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
logger.error('Cannot access iframe document');
|
|
5122
|
-
config.onError?.(new Error('Cannot access iframe document'));
|
|
5623
|
+
async function initialize(s) {
|
|
5624
|
+
if (!s.iframe || !s.config) {
|
|
5625
|
+
s.logger.error('iframe not found');
|
|
5626
|
+
s.config?.onError?.(new Error('iframe not found'));
|
|
5123
5627
|
return;
|
|
5124
5628
|
}
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
start$3(iframe, config);
|
|
5128
|
-
start$5(iframe, { debug: config.debug });
|
|
5129
|
-
const result = await run();
|
|
5130
|
-
logger.log('Process completed:', result);
|
|
5131
|
-
config.onSuccess?.(result);
|
|
5132
|
-
dispatchDimensionsEvent(result);
|
|
5133
|
-
// Optionally setup height observer
|
|
5134
|
-
// setupHeightObserver();
|
|
5629
|
+
if (s.iframe.contentDocument?.readyState === 'complete') {
|
|
5630
|
+
await process(s);
|
|
5135
5631
|
}
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5632
|
+
else {
|
|
5633
|
+
s.loadListener = () => process(s);
|
|
5634
|
+
s.iframe.addEventListener('load', s.loadListener);
|
|
5139
5635
|
}
|
|
5140
5636
|
}
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5637
|
+
function start$1(s, cfg) {
|
|
5638
|
+
if (s.running) {
|
|
5639
|
+
s.logger.warn('Fixer is already running. Call stop() first.');
|
|
5640
|
+
return;
|
|
5641
|
+
}
|
|
5642
|
+
s.iframe = cfg.iframe;
|
|
5643
|
+
s.config = cfg;
|
|
5644
|
+
s.running = true;
|
|
5645
|
+
s.logger.configure({ enabled: !!cfg.debug });
|
|
5646
|
+
s.logger.log('Iframe fixer started');
|
|
5647
|
+
initialize(s);
|
|
5148
5648
|
}
|
|
5149
|
-
function
|
|
5150
|
-
|
|
5649
|
+
function stop$1(s) {
|
|
5650
|
+
if (!s.running)
|
|
5651
|
+
return;
|
|
5652
|
+
s.viewportReplacer.stop();
|
|
5653
|
+
s.heightObserver.stop();
|
|
5654
|
+
s.navigationBlocker.stop();
|
|
5655
|
+
if (s.iframe && s.loadListener) {
|
|
5656
|
+
s.iframe.removeEventListener('load', s.loadListener);
|
|
5657
|
+
s.loadListener = null;
|
|
5658
|
+
}
|
|
5659
|
+
s.iframe = null;
|
|
5660
|
+
s.config = null;
|
|
5661
|
+
s.running = false;
|
|
5662
|
+
s.logger.log('Iframe fixer stopped');
|
|
5663
|
+
}
|
|
5664
|
+
async function recalculate$1(s) {
|
|
5665
|
+
if (!s.running) {
|
|
5666
|
+
s.logger.warn('Fixer is not running.');
|
|
5667
|
+
return;
|
|
5668
|
+
}
|
|
5669
|
+
s.logger.log('Recalculating...');
|
|
5670
|
+
await process(s);
|
|
5151
5671
|
}
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
function enableNavigationBlocking$2() {
|
|
5156
|
-
if (!running$1) {
|
|
5157
|
-
logger.warn('Fixer is not running. Call start() first.');
|
|
5672
|
+
function updateConfig(s, cfg) {
|
|
5673
|
+
if (!s.running || !s.config) {
|
|
5674
|
+
s.logger.warn('Fixer is not running.');
|
|
5158
5675
|
return;
|
|
5159
5676
|
}
|
|
5160
|
-
|
|
5677
|
+
s.config = { ...s.config, ...cfg };
|
|
5678
|
+
s.viewportReplacer.updateConfig(cfg);
|
|
5679
|
+
s.logger.log('Config updated');
|
|
5680
|
+
}
|
|
5681
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
5682
|
+
function createOrchestrator() {
|
|
5683
|
+
const s = {
|
|
5684
|
+
logger: createLogger({ enabled: false, prefix: 'IframeFixer' }),
|
|
5685
|
+
viewportReplacer: createViewportProcessor(),
|
|
5686
|
+
navigationBlocker: createNavigationBlocker(),
|
|
5687
|
+
heightObserver: createHeightObserver(),
|
|
5688
|
+
iframe: null,
|
|
5689
|
+
config: null,
|
|
5690
|
+
running: false,
|
|
5691
|
+
loadListener: null,
|
|
5692
|
+
};
|
|
5693
|
+
return {
|
|
5694
|
+
start: (cfg) => start$1(s, cfg),
|
|
5695
|
+
stop: () => stop$1(s),
|
|
5696
|
+
recalculate: () => recalculate$1(s),
|
|
5697
|
+
updateConfig: (cfg) => updateConfig(s, cfg),
|
|
5698
|
+
enableNavigationBlocking: () => s.navigationBlocker.enable(),
|
|
5699
|
+
enableNavigationBlockingMessage: () => s.navigationBlocker.enableMessage(),
|
|
5700
|
+
disableNavigationBlocking: () => s.navigationBlocker.disable(),
|
|
5701
|
+
disableNavigationBlockingMessage: () => s.navigationBlocker.disableMessage(),
|
|
5702
|
+
isRunning: () => s.running,
|
|
5703
|
+
getStateInfo: () => ({
|
|
5704
|
+
isRunning: s.running,
|
|
5705
|
+
hasIframe: !!s.iframe,
|
|
5706
|
+
hasConfig: !!s.config,
|
|
5707
|
+
hasNavigationBlocker: s.navigationBlocker.isRunning(),
|
|
5708
|
+
hasHeightObserver: s.heightObserver.isRunning(),
|
|
5709
|
+
viewportReplacerRunning: s.viewportReplacer.isRunning(),
|
|
5710
|
+
}),
|
|
5711
|
+
};
|
|
5161
5712
|
}
|
|
5162
5713
|
|
|
5163
5714
|
/**
|
|
5164
|
-
* Iframe Helper
|
|
5165
|
-
*
|
|
5715
|
+
* Iframe Helper — factory entry point.
|
|
5716
|
+
*
|
|
5717
|
+
* Each call to `createIframeHelper()` returns a fully isolated instance
|
|
5718
|
+
* with its own processor state. Use one instance per iframe.
|
|
5166
5719
|
*/
|
|
5167
|
-
//
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
// ============================================================================
|
|
5172
|
-
// Public API
|
|
5173
|
-
// ============================================================================
|
|
5174
|
-
function start$1(config) {
|
|
5175
|
-
if (running) {
|
|
5176
|
-
console.warn('[IframeHelperStarter] Already running. Call stop() first.');
|
|
5720
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
5721
|
+
function start(s, config) {
|
|
5722
|
+
if (s.running) {
|
|
5723
|
+
console.warn('[IframeHelper] Already running. Call stop() first.');
|
|
5177
5724
|
return;
|
|
5178
5725
|
}
|
|
5179
|
-
start
|
|
5180
|
-
running = true;
|
|
5726
|
+
s.fixer.start(config);
|
|
5727
|
+
s.running = true;
|
|
5181
5728
|
}
|
|
5182
|
-
function stop
|
|
5183
|
-
if (!running)
|
|
5729
|
+
function stop(s) {
|
|
5730
|
+
if (!s.running)
|
|
5184
5731
|
return;
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
running = false;
|
|
5732
|
+
s.fixer.stop();
|
|
5733
|
+
s.running = false;
|
|
5188
5734
|
}
|
|
5189
|
-
function
|
|
5190
|
-
if (!running) {
|
|
5191
|
-
console.warn('[
|
|
5735
|
+
async function recalculate(s) {
|
|
5736
|
+
if (!s.running) {
|
|
5737
|
+
console.warn('[IframeHelper] Not running. Call start() first.');
|
|
5192
5738
|
return;
|
|
5193
5739
|
}
|
|
5194
|
-
|
|
5740
|
+
await s.fixer.recalculate();
|
|
5195
5741
|
}
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5742
|
+
function enableNavigationBlocking(s) {
|
|
5743
|
+
if (!s.running) {
|
|
5744
|
+
console.warn('[IframeHelper] Not running. Call start() first.');
|
|
5745
|
+
return;
|
|
5746
|
+
}
|
|
5747
|
+
s.fixer.enableNavigationBlocking();
|
|
5202
5748
|
}
|
|
5203
|
-
|
|
5204
|
-
|
|
5749
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
5750
|
+
function createIframeHelper() {
|
|
5751
|
+
const s = {
|
|
5752
|
+
fixer: createOrchestrator(),
|
|
5753
|
+
running: false,
|
|
5754
|
+
};
|
|
5755
|
+
return {
|
|
5756
|
+
start: (config) => start(s, config),
|
|
5757
|
+
stop: () => stop(s),
|
|
5758
|
+
recalculate: () => recalculate(s),
|
|
5759
|
+
enableNavigationBlocking: () => enableNavigationBlocking(s),
|
|
5760
|
+
isRunning: () => s.running,
|
|
5761
|
+
};
|
|
5205
5762
|
}
|
|
5206
5763
|
|
|
5764
|
+
const iframeHelper = createIframeHelper();
|
|
5207
5765
|
function useVizLiveRender() {
|
|
5208
5766
|
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
5209
5767
|
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
5210
5768
|
const wrapperWidth = useHeatmapVizRectContext((s) => s.wrapperWidth);
|
|
5211
|
-
const
|
|
5212
|
-
const htmlContent =
|
|
5213
|
-
const targetUrl =
|
|
5769
|
+
const setIsRenderedViz = useHeatmapVizContext((s) => s.setIsRenderedViz);
|
|
5770
|
+
const htmlContent = useHeatmapLiveContext((s) => s.htmlContent);
|
|
5771
|
+
const targetUrl = useHeatmapLiveContext((s) => s.targetUrl);
|
|
5214
5772
|
const deviceType = useHeatmapSettingContext((s) => s.deviceType);
|
|
5215
|
-
const renderMode =
|
|
5216
|
-
const storefrontPassword =
|
|
5773
|
+
const renderMode = useHeatmapLiveContext((s) => s.renderMode);
|
|
5774
|
+
const storefrontPassword = useHeatmapLiveContext((s) => s.storefrontPassword);
|
|
5217
5775
|
useHeatmapWidthByDevice();
|
|
5218
5776
|
const { iframeRef, isReady } = useVizLiveIframeMsg();
|
|
5219
5777
|
// Handle iframe rendering based on mode
|
|
@@ -5254,10 +5812,10 @@ function useVizLiveRender() {
|
|
|
5254
5812
|
const hasContent = (renderMode === 'portal' && targetUrl) || (renderMode === 'inline' && htmlContent);
|
|
5255
5813
|
if (!iframe || !hasContent)
|
|
5256
5814
|
return;
|
|
5257
|
-
|
|
5258
|
-
|
|
5815
|
+
setIsRenderedViz(true);
|
|
5816
|
+
startIframe$1(iframe, deviceType, { width: wrapperWidth, height: wrapperHeight }, (height) => {
|
|
5259
5817
|
height && setIframeHeight(height);
|
|
5260
|
-
|
|
5818
|
+
setIsRenderedViz(true);
|
|
5261
5819
|
});
|
|
5262
5820
|
return () => { };
|
|
5263
5821
|
}, [
|
|
@@ -5269,7 +5827,7 @@ function useVizLiveRender() {
|
|
|
5269
5827
|
targetUrl,
|
|
5270
5828
|
htmlContent,
|
|
5271
5829
|
iframeRef,
|
|
5272
|
-
|
|
5830
|
+
setIsRenderedViz,
|
|
5273
5831
|
setIframeHeight,
|
|
5274
5832
|
]);
|
|
5275
5833
|
return {
|
|
@@ -5288,9 +5846,9 @@ function buildPortalUrl(targetUrl, storefrontPassword) {
|
|
|
5288
5846
|
const portalServiceUrl = getPortalServiceUrl();
|
|
5289
5847
|
return `${portalServiceUrl}/?${params.toString()}`;
|
|
5290
5848
|
}
|
|
5291
|
-
function
|
|
5292
|
-
stop();
|
|
5293
|
-
start({
|
|
5849
|
+
function startIframe$1(iframe, deviceType = EDeviceType.Desktop, rect, onSuccess) {
|
|
5850
|
+
iframeHelper.stop();
|
|
5851
|
+
iframeHelper.start({
|
|
5294
5852
|
deviceType: deviceType,
|
|
5295
5853
|
targetWidth: rect.width,
|
|
5296
5854
|
targetHeight: rect.height,
|
|
@@ -5301,7 +5859,7 @@ function initIframeHelper$1(iframe, deviceType = EDeviceType.Desktop, rect, onSu
|
|
|
5301
5859
|
},
|
|
5302
5860
|
});
|
|
5303
5861
|
// fixer.recalculate();
|
|
5304
|
-
enableNavigationBlocking();
|
|
5862
|
+
iframeHelper.enableNavigationBlocking();
|
|
5305
5863
|
}
|
|
5306
5864
|
|
|
5307
5865
|
const CANVAS_ID = 'clarity-heatmap-canvas';
|
|
@@ -5551,36 +6109,69 @@ class GXVisualizer extends Visualizer {
|
|
|
5551
6109
|
};
|
|
5552
6110
|
}
|
|
5553
6111
|
|
|
6112
|
+
// ── Performance timing ────────────────────────────────────────────────────────
|
|
6113
|
+
function mark(label) {
|
|
6114
|
+
const t = performance.now();
|
|
6115
|
+
console.log(`[Render] ⏱ ${label}`);
|
|
6116
|
+
return t;
|
|
6117
|
+
}
|
|
6118
|
+
function measure(label, startMs) {
|
|
6119
|
+
const ms = (performance.now() - startMs).toFixed(1);
|
|
6120
|
+
console.log(`[Render] ✅ ${label} — ${ms}ms`);
|
|
6121
|
+
}
|
|
6122
|
+
// ── Hook ──────────────────────────────────────────────────────────────────────
|
|
5554
6123
|
const useHeatmapRender = () => {
|
|
5555
6124
|
const viewId = useViewIdContext();
|
|
5556
6125
|
const data = useHeatmapDataContext((s) => s.data);
|
|
6126
|
+
const shopId = useHeatmapConfigStore((s) => s.shopId);
|
|
5557
6127
|
const vizRef = useHeatmapVizRectContext((s) => s.vizRef);
|
|
5558
6128
|
const setVizRef = useHeatmapVizRectContext((s) => s.setVizRef);
|
|
5559
6129
|
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
5560
|
-
const
|
|
6130
|
+
const setIsRenderedViz = useHeatmapVizContext((s) => s.setIsRenderedViz);
|
|
5561
6131
|
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
5562
6132
|
const contentWidth = useHeatmapWidthByDevice();
|
|
5563
6133
|
const deviceType = useHeatmapSettingContext((s) => s.deviceType);
|
|
5564
6134
|
const iframeRef = useRef(null);
|
|
6135
|
+
const helperRef = useRef(null);
|
|
6136
|
+
const abortRef = useRef(null);
|
|
5565
6137
|
const renderHeatmap = useCallback(async (payloads) => {
|
|
5566
6138
|
if (contentWidth === 0 || wrapperHeight === 0)
|
|
5567
6139
|
return;
|
|
5568
6140
|
if (!payloads || payloads.length === 0)
|
|
5569
6141
|
return;
|
|
6142
|
+
// Cancel any in-flight render before starting a new one
|
|
6143
|
+
abortRef.current?.abort();
|
|
6144
|
+
const abort = new AbortController();
|
|
6145
|
+
abortRef.current = abort;
|
|
6146
|
+
const t0 = mark('renderHeatmap start');
|
|
5570
6147
|
const visualizer = vizRef ?? new GXVisualizer();
|
|
5571
6148
|
if (!vizRef)
|
|
5572
6149
|
setVizRef(visualizer);
|
|
5573
|
-
|
|
6150
|
+
setIsRenderedViz(false);
|
|
5574
6151
|
const iframe = iframeRef.current;
|
|
5575
6152
|
if (!iframe?.contentWindow)
|
|
5576
6153
|
return;
|
|
6154
|
+
const tHtml = mark('visualizer.html start');
|
|
5577
6155
|
await visualizer.html(payloads, iframe.contentWindow, viewId);
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
6156
|
+
measure('visualizer.html', tHtml);
|
|
6157
|
+
// Bail out if data changed or component unmounted while we were awaiting
|
|
6158
|
+
if (abort.signal.aborted)
|
|
6159
|
+
return;
|
|
6160
|
+
startIframe({
|
|
6161
|
+
helperRef,
|
|
6162
|
+
iframe,
|
|
6163
|
+
shopId,
|
|
6164
|
+
deviceType,
|
|
6165
|
+
size: { width: contentWidth, height: wrapperHeight },
|
|
6166
|
+
t0,
|
|
6167
|
+
onSuccess: (height) => {
|
|
6168
|
+
if (abort.signal.aborted)
|
|
6169
|
+
return;
|
|
6170
|
+
if (height)
|
|
6171
|
+
setIframeHeight(height);
|
|
6172
|
+
setIsRenderedViz(true);
|
|
6173
|
+
},
|
|
5582
6174
|
});
|
|
5583
|
-
// setIsRenderViz(true);
|
|
5584
6175
|
}, [wrapperHeight, contentWidth, deviceType]);
|
|
5585
6176
|
useEffect(() => {
|
|
5586
6177
|
if (!data || data.length === 0)
|
|
@@ -5591,33 +6182,47 @@ const useHeatmapRender = () => {
|
|
|
5591
6182
|
setVizRef(null);
|
|
5592
6183
|
};
|
|
5593
6184
|
}, [data, renderHeatmap, setVizRef]);
|
|
6185
|
+
// Abort render + stop helper when the component unmounts
|
|
6186
|
+
useEffect(() => {
|
|
6187
|
+
return () => {
|
|
6188
|
+
abortRef.current?.abort();
|
|
6189
|
+
helperRef.current?.stop();
|
|
6190
|
+
helperRef.current = null;
|
|
6191
|
+
};
|
|
6192
|
+
}, []);
|
|
5594
6193
|
return {
|
|
5595
6194
|
iframeRef,
|
|
5596
6195
|
};
|
|
5597
6196
|
};
|
|
5598
|
-
|
|
6197
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
6198
|
+
function startIframe({ helperRef, iframe, shopId, deviceType = EDeviceType.Desktop, size, t0, onSuccess, }) {
|
|
5599
6199
|
const docWidth = size.width ?? 0;
|
|
5600
6200
|
const docHeight = size.height ?? 0;
|
|
5601
6201
|
if (docHeight === 0)
|
|
5602
6202
|
return;
|
|
5603
|
-
stop();
|
|
5604
|
-
start
|
|
5605
|
-
|
|
6203
|
+
helperRef.current?.stop();
|
|
6204
|
+
const tHelper = mark('IframeHelper.start');
|
|
6205
|
+
const helper = createIframeHelper();
|
|
6206
|
+
helperRef.current = helper;
|
|
6207
|
+
helper.start({
|
|
6208
|
+
deviceType,
|
|
5606
6209
|
targetWidth: docWidth,
|
|
5607
6210
|
targetHeight: docHeight,
|
|
5608
|
-
iframe
|
|
6211
|
+
iframe,
|
|
5609
6212
|
debug: true,
|
|
6213
|
+
shopId,
|
|
5610
6214
|
onSuccess: (data) => {
|
|
6215
|
+
measure('IframeHelper processing', tHelper);
|
|
6216
|
+
measure('Total render', t0);
|
|
5611
6217
|
iframe.style.height = `${data.height}px`;
|
|
5612
6218
|
onSuccess(data.height);
|
|
5613
6219
|
},
|
|
5614
6220
|
});
|
|
5615
|
-
// fixer.recalculate();
|
|
5616
6221
|
}
|
|
5617
6222
|
|
|
5618
6223
|
const useReplayRender = () => {
|
|
5619
6224
|
const data = useHeatmapDataContext((s) => s.data);
|
|
5620
|
-
const
|
|
6225
|
+
const setIsRenderedViz = useHeatmapVizContext((s) => s.setIsRenderedViz);
|
|
5621
6226
|
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
5622
6227
|
const visualizerRef = useRef(null);
|
|
5623
6228
|
const iframeRef = useRef(null);
|
|
@@ -5637,7 +6242,7 @@ const useReplayRender = () => {
|
|
|
5637
6242
|
version: envelope.version,
|
|
5638
6243
|
onresize: (height) => {
|
|
5639
6244
|
height && setIframeHeight(height);
|
|
5640
|
-
|
|
6245
|
+
setIsRenderedViz(true);
|
|
5641
6246
|
},
|
|
5642
6247
|
mobile,
|
|
5643
6248
|
vNext: true,
|
|
@@ -5793,7 +6398,7 @@ const useContentDimensions = ({ iframeRef }) => {
|
|
|
5793
6398
|
const useObserveIframeHeight = (props) => {
|
|
5794
6399
|
const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
5795
6400
|
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
5796
|
-
const
|
|
6401
|
+
const isRenderedViz = useHeatmapVizContext((s) => s.isRenderedViz);
|
|
5797
6402
|
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
5798
6403
|
const { iframeRef } = props;
|
|
5799
6404
|
const resizeObserverRef = useRef(null);
|
|
@@ -5854,7 +6459,7 @@ const useObserveIframeHeight = (props) => {
|
|
|
5854
6459
|
}, [updateIframeHeight]);
|
|
5855
6460
|
useEffect(() => {
|
|
5856
6461
|
const iframe = iframeRef.current;
|
|
5857
|
-
if (!iframe || !iframeHeight || !
|
|
6462
|
+
if (!iframe || !iframeHeight || !isRenderedViz)
|
|
5858
6463
|
return;
|
|
5859
6464
|
const setupObservers = () => {
|
|
5860
6465
|
try {
|
|
@@ -5916,7 +6521,7 @@ const useObserveIframeHeight = (props) => {
|
|
|
5916
6521
|
}
|
|
5917
6522
|
iframe.removeEventListener('load', setupObservers);
|
|
5918
6523
|
};
|
|
5919
|
-
}, [iframeRef, iframeHeight,
|
|
6524
|
+
}, [iframeRef, iframeHeight, isRenderedViz]);
|
|
5920
6525
|
return {};
|
|
5921
6526
|
};
|
|
5922
6527
|
|
|
@@ -6215,10 +6820,10 @@ const useScrollmapZones = (options) => {
|
|
|
6215
6820
|
const newZones = createZones(scrollmap);
|
|
6216
6821
|
setZones(newZones);
|
|
6217
6822
|
setIsReady(true);
|
|
6218
|
-
logger$
|
|
6823
|
+
logger$4.log(`[useScrollmap] Created ${newZones.length} zones in ${mode} mode`);
|
|
6219
6824
|
}
|
|
6220
6825
|
catch (error) {
|
|
6221
|
-
logger$
|
|
6826
|
+
logger$4.error('[useScrollmap] Error:', error);
|
|
6222
6827
|
setIsReady(false);
|
|
6223
6828
|
}
|
|
6224
6829
|
}, [enabled, scrollmap, mode, createZones]);
|
|
@@ -7048,11 +7653,11 @@ const AutoScrollHandler = ({ visualRef }) => {
|
|
|
7048
7653
|
};
|
|
7049
7654
|
|
|
7050
7655
|
const PortalAreaRenderer = ({ iframeRef, visualRef, shadowRoot, onAreaCreated, onAreaClick, }) => {
|
|
7051
|
-
const
|
|
7656
|
+
const isRenderedViz = useHeatmapVizContext((s) => s.isRenderedViz);
|
|
7052
7657
|
const iframeDocument = iframeRef.current?.contentDocument || undefined;
|
|
7053
7658
|
const { shadowContainer, isReady } = useAreaRendererContainer(iframeDocument, shadowRoot);
|
|
7054
|
-
useAreaRectSync({ iframeDocument, shadowRoot, enabled: isReady &&
|
|
7055
|
-
useAreaPositionsUpdater({ iframeRef, visualRef, enabled: isReady &&
|
|
7659
|
+
useAreaRectSync({ iframeDocument, shadowRoot, enabled: isReady && isRenderedViz });
|
|
7660
|
+
useAreaPositionsUpdater({ iframeRef, visualRef, enabled: isReady && isRenderedViz });
|
|
7056
7661
|
if (!shadowContainer || !isReady)
|
|
7057
7662
|
return null;
|
|
7058
7663
|
return (jsxs(Fragment$1, { children: [jsx(AutoScrollHandler, { visualRef: visualRef }), jsx(AreasPortal, { shadowContainer: shadowContainer, onAreaClick: onAreaClick }), jsx(AreaEditHighlightPortal, { shadowContainer: shadowContainer, iframeRef: iframeRef, customShadowRoot: shadowRoot, onAreaCreated: onAreaCreated })] }));
|
|
@@ -7061,7 +7666,7 @@ const PortalAreaRenderer = ({ iframeRef, visualRef, shadowRoot, onAreaCreated, o
|
|
|
7061
7666
|
const VizAreaClick = ({ iframeRef, visualRef, shadowRoot, autoCreateTopN = 10, enableOverlapResolution = true, onAreaClick, }) => {
|
|
7062
7667
|
const clickAreas = useHeatmapDataContext((s) => s.clickAreas);
|
|
7063
7668
|
const resetView = useHeatmapAreaClickContext((s) => s.resetView);
|
|
7064
|
-
const
|
|
7669
|
+
const isRenderedViz = useHeatmapVizContext((s) => s.isRenderedViz);
|
|
7065
7670
|
useAreaTopAutoDetect({ autoCreateTopN, shadowRoot, disabled: !!clickAreas?.length });
|
|
7066
7671
|
useAreaFilterVisible({ iframeRef, enableOverlapResolution });
|
|
7067
7672
|
useAreaHydration({ shadowRoot });
|
|
@@ -7070,7 +7675,7 @@ const VizAreaClick = ({ iframeRef, visualRef, shadowRoot, autoCreateTopN = 10, e
|
|
|
7070
7675
|
resetView();
|
|
7071
7676
|
};
|
|
7072
7677
|
}, []);
|
|
7073
|
-
if (!iframeRef.current || !
|
|
7678
|
+
if (!iframeRef.current || !isRenderedViz)
|
|
7074
7679
|
return null;
|
|
7075
7680
|
return (jsx(Fragment, { children: jsx(PortalAreaRenderer, { iframeRef: iframeRef, visualRef: visualRef, shadowRoot: shadowRoot, onAreaClick: onAreaClick }) }));
|
|
7076
7681
|
};
|
|
@@ -7784,7 +8389,7 @@ const VizDomHeatmap = () => {
|
|
|
7784
8389
|
const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
7785
8390
|
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
7786
8391
|
const setVizRef = useHeatmapVizRectContext((s) => s.setVizRef);
|
|
7787
|
-
const
|
|
8392
|
+
const setIsRenderedViz = useHeatmapVizContext((s) => s.setIsRenderedViz);
|
|
7788
8393
|
const setSelectedElement = useHeatmapClickContext((s) => s.setSelectedElement);
|
|
7789
8394
|
const setHoveredElement = useHeatmapHoverContext((s) => s.setHoveredElement);
|
|
7790
8395
|
// const setSelectedArea = useHeatmapAreaClickContext((s) => s.setSelectedArea);
|
|
@@ -7794,7 +8399,7 @@ const VizDomHeatmap = () => {
|
|
|
7794
8399
|
const cleanUp = () => {
|
|
7795
8400
|
setVizRef(null);
|
|
7796
8401
|
setIframeHeight(0);
|
|
7797
|
-
|
|
8402
|
+
setIsRenderedViz(false);
|
|
7798
8403
|
setSelectedElement(null);
|
|
7799
8404
|
setHoveredElement(null);
|
|
7800
8405
|
// setSelectedArea(null);
|
|
@@ -7826,7 +8431,7 @@ const VizLiveRenderer = () => {
|
|
|
7826
8431
|
const VizLiveHeatmap = () => {
|
|
7827
8432
|
const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
7828
8433
|
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
7829
|
-
|
|
8434
|
+
useHeatmapLiveContext((s) => s.reset);
|
|
7830
8435
|
const CompVizLoading = useHeatmapControlStore((state) => state.controls.VizLoading);
|
|
7831
8436
|
// TODO: Remove this after testing
|
|
7832
8437
|
useEffect(() => {
|
|
@@ -7877,11 +8482,11 @@ const ContentTopBar = () => {
|
|
|
7877
8482
|
}, children: CompTopBar && jsx(CompTopBar, {}) }));
|
|
7878
8483
|
};
|
|
7879
8484
|
|
|
7880
|
-
const HeatmapLayout = ({ data, clickmap, clickAreas, scrollmap, attentionMap, controls, dataInfo, isLoading, isLoadingCanvas, }) => {
|
|
8485
|
+
const HeatmapLayout = ({ shopId, data, clickmap, clickAreas, scrollmap, attentionMap, controls, dataInfo, isLoading, isLoadingCanvas, }) => {
|
|
7881
8486
|
useRegisterControl(controls);
|
|
7882
8487
|
useRegisterData(data, dataInfo);
|
|
7883
8488
|
useRegisterHeatmap({ clickmap, scrollmap, clickAreas, attentionMap });
|
|
7884
|
-
useRegisterConfig({ isLoading, isLoadingCanvas });
|
|
8489
|
+
useRegisterConfig({ isLoading, isLoadingCanvas, shopId });
|
|
7885
8490
|
// performanceLogger.configure({
|
|
7886
8491
|
// enabled: true,
|
|
7887
8492
|
// logToConsole: false,
|
|
@@ -7911,4 +8516,4 @@ const HeatmapLayout = ({ data, clickmap, clickAreas, scrollmap, attentionMap, co
|
|
|
7911
8516
|
}
|
|
7912
8517
|
};
|
|
7913
8518
|
|
|
7914
|
-
export { BACKDROP_CONFIG, DEFAULT_SIDEBAR_WIDTH, DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO, EClickMode, EClickRankType, EClickType, EDeviceType, EHeatmapMode, EHeatmapType, ELM_CALLOUT_CONFIG, EScrollType, GraphView, HEATMAP_CONFIG, HEATMAP_IFRAME, HEATMAP_STYLE, HeatmapLayout, ViewIdContext, Z_INDEX$1 as Z_INDEX, compareViewPerformance, convertViewportToIframeCoords, createStorePerformanceTracker, createViewContextHook, decodeArrayClarity, decodeClarity, downloadPerformanceReport, getCompareViewId, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, performanceLogger, printPerformanceSummary, scrollToElementIfNeeded, sendPerformanceReport, serializeAreas, trackStoreAction, useAreaCreation, useAreaEditMode, useAreaFilterVisible, useAreaHydration, useAreaInteraction, useAreaPositionsUpdater, useAreaRectSync, useAreaRendererContainer, useAreaTopAutoDetect, useClickedElement, useDebounceCallback, useElementCalloutVisible, useHeatmapAreaClickContext, useHeatmapCanvas, useHeatmapClickContext, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapDataContext, useHeatmapEffects, useHeatmapElementPosition, useHeatmapHoverContext,
|
|
8519
|
+
export { BACKDROP_CONFIG, DEFAULT_SIDEBAR_WIDTH, DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO, EClickMode, EClickRankType, EClickType, EDeviceType, EHeatmapDataSource, EHeatmapMode, EHeatmapType, ELM_CALLOUT_CONFIG, EScrollType, GraphView, HEATMAP_CONFIG, HEATMAP_IFRAME, HEATMAP_STYLE, HeatmapLayout, ViewIdContext, Z_INDEX$1 as Z_INDEX, compareViewPerformance, convertViewportToIframeCoords, createStorePerformanceTracker, createViewContextHook, decodeArrayClarity, decodeClarity, downloadPerformanceReport, getCompareViewId, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, performanceLogger, printPerformanceSummary, scrollToElementIfNeeded, sendPerformanceReport, serializeAreas, trackStoreAction, useAreaCreation, useAreaEditMode, useAreaFilterVisible, useAreaHydration, useAreaInteraction, useAreaPositionsUpdater, useAreaRectSync, useAreaRendererContainer, useAreaTopAutoDetect, useClickedElement, useDebounceCallback, useElementCalloutVisible, useHeatmapAreaClickContext, useHeatmapCanvas, useHeatmapClickContext, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapDataContext, useHeatmapEffects, useHeatmapElementPosition, useHeatmapHoverContext, useHeatmapLiveContext, useHeatmapRenderByMode, useHeatmapScale, useHeatmapScroll, useHeatmapScrollContext, useHeatmapSettingContext, useHeatmapVizContext, useHeatmapVizRectContext, useHeatmapWidthByDevice, useHoveredElement, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewIdContext, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };
|