@gemx-dev/heatmap-react 3.5.92-dev.2 → 3.5.92-dev.21
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 +2 -0
- package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -1
- package/dist/esm/components/VizDom/VizDomHeatmap.d.ts.map +1 -1
- package/dist/esm/components/VizDom/VizDomRenderer.d.ts +2 -1
- package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
- package/dist/esm/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
- package/dist/esm/hooks/common/index.d.ts +1 -1
- package/dist/esm/hooks/common/index.d.ts.map +1 -1
- package/dist/esm/hooks/common/useHeatmapViewportByDevice.d.ts +7 -0
- package/dist/esm/hooks/common/useHeatmapViewportByDevice.d.ts.map +1 -0
- package/dist/esm/hooks/register/useRegisterConfig.d.ts +3 -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/viz-canvas/useScrollmap.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/useHeatmapIframeProcessor.d.ts +6 -0
- package/dist/esm/hooks/viz-render/useHeatmapIframeProcessor.d.ts.map +1 -0
- package/dist/esm/hooks/viz-render/useHeatmapRenderByMode.d.ts +3 -2
- package/dist/esm/hooks/viz-render/useHeatmapRenderByMode.d.ts.map +1 -1
- package/dist/esm/hooks/viz-render/useHeatmapRenderDom.d.ts +5 -0
- package/dist/esm/hooks/viz-render/useHeatmapRenderDom.d.ts.map +1 -0
- package/dist/esm/hooks/viz-render/useReplayRender.d.ts +2 -2
- package/dist/esm/hooks/viz-render/useReplayRender.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useScaleCalculation.d.ts +1 -2
- package/dist/esm/hooks/viz-scale/useScaleCalculation.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 +2027 -1021
- package/dist/esm/index.mjs +2027 -1021
- 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 +5 -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/listeners.d.ts +11 -0
- package/dist/esm/libs/iframe-processor/processors/height-observer/listeners.d.ts.map +1 -0
- package/dist/esm/libs/iframe-processor/processors/height-observer/types.d.ts +14 -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/index.d.ts +0 -1
- package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/index.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/libs/visualizer/GXVisualizer.d.ts +23 -4
- package/dist/esm/libs/visualizer/GXVisualizer.d.ts.map +1 -1
- package/dist/esm/libs/visualizer/cache/config.d.ts +15 -0
- package/dist/esm/libs/visualizer/cache/config.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/cache/index.d.ts +16 -0
- package/dist/esm/libs/visualizer/cache/index.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/index.d.ts +2 -2
- package/dist/esm/libs/visualizer/index.d.ts.map +1 -1
- package/dist/{umd/libs/visualizer → esm/libs/visualizer/renderers}/AttentionMapRenderer.d.ts +2 -2
- package/dist/esm/libs/visualizer/renderers/AttentionMapRenderer.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/renderers/ScrollBucketRenderer.d.ts +44 -0
- package/dist/esm/libs/visualizer/renderers/ScrollBucketRenderer.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/renderers/index.d.ts +3 -0
- package/dist/esm/libs/visualizer/renderers/index.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/shadow-dom/extractor.d.ts +23 -0
- package/dist/esm/libs/visualizer/shadow-dom/extractor.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/types/data.d.ts +72 -0
- package/dist/esm/libs/visualizer/types/data.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/{types.d.ts → types/extend.d.ts} +7 -2
- package/dist/esm/libs/visualizer/types/extend.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/types/index.d.ts +6 -0
- package/dist/esm/libs/visualizer/types/index.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/types/layout.d.ts +69 -0
- package/dist/esm/libs/visualizer/types/layout.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/types/visualize.d.ts +26 -0
- package/dist/esm/libs/visualizer/types/visualize.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/utils/delay.d.ts +21 -0
- package/dist/esm/libs/visualizer/utils/delay.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/utils/render.d.ts +7 -0
- package/dist/esm/libs/visualizer/utils/render.d.ts.map +1 -0
- package/dist/esm/stores/config.d.ts +4 -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/types/clarity.d.ts +18 -0
- package/dist/esm/types/clarity.d.ts.map +1 -1
- package/dist/esm/types/control.d.ts +2 -0
- package/dist/esm/types/control.d.ts.map +1 -1
- package/dist/esm/types/heatmap-info.d.ts +1 -0
- package/dist/esm/types/heatmap-info.d.ts.map +1 -1
- package/dist/esm/types/heatmap.d.ts +9 -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/esm/types/viz-scrollmap.d.ts +4 -0
- package/dist/esm/types/viz-scrollmap.d.ts.map +1 -1
- package/dist/esm/utils/retry.d.ts +1 -0
- package/dist/esm/utils/retry.d.ts.map +1 -1
- package/dist/umd/components/Layout/HeatmapLayout.d.ts +2 -0
- package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
- package/dist/umd/components/VizDom/VizDomHeatmap.d.ts.map +1 -1
- package/dist/umd/components/VizDom/VizDomRenderer.d.ts +2 -1
- package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
- package/dist/umd/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
- package/dist/umd/hooks/common/index.d.ts +1 -1
- package/dist/umd/hooks/common/index.d.ts.map +1 -1
- package/dist/umd/hooks/common/useHeatmapViewportByDevice.d.ts +7 -0
- package/dist/umd/hooks/common/useHeatmapViewportByDevice.d.ts.map +1 -0
- package/dist/umd/hooks/register/useRegisterConfig.d.ts +3 -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/viz-canvas/useScrollmap.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/useHeatmapIframeProcessor.d.ts +6 -0
- package/dist/umd/hooks/viz-render/useHeatmapIframeProcessor.d.ts.map +1 -0
- package/dist/umd/hooks/viz-render/useHeatmapRenderByMode.d.ts +3 -2
- package/dist/umd/hooks/viz-render/useHeatmapRenderByMode.d.ts.map +1 -1
- package/dist/umd/hooks/viz-render/useHeatmapRenderDom.d.ts +5 -0
- package/dist/umd/hooks/viz-render/useHeatmapRenderDom.d.ts.map +1 -0
- package/dist/umd/hooks/viz-render/useReplayRender.d.ts +2 -2
- package/dist/umd/hooks/viz-render/useReplayRender.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useScaleCalculation.d.ts +1 -2
- package/dist/umd/hooks/viz-scale/useScaleCalculation.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 +5 -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/listeners.d.ts +11 -0
- package/dist/umd/libs/iframe-processor/processors/height-observer/listeners.d.ts.map +1 -0
- package/dist/umd/libs/iframe-processor/processors/height-observer/types.d.ts +14 -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/index.d.ts +0 -1
- package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/index.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/libs/visualizer/GXVisualizer.d.ts +23 -4
- package/dist/umd/libs/visualizer/GXVisualizer.d.ts.map +1 -1
- package/dist/umd/libs/visualizer/cache/config.d.ts +15 -0
- package/dist/umd/libs/visualizer/cache/config.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/cache/index.d.ts +16 -0
- package/dist/umd/libs/visualizer/cache/index.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/index.d.ts +2 -2
- package/dist/umd/libs/visualizer/index.d.ts.map +1 -1
- package/dist/{esm/libs/visualizer → umd/libs/visualizer/renderers}/AttentionMapRenderer.d.ts +2 -2
- package/dist/umd/libs/visualizer/renderers/AttentionMapRenderer.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/renderers/ScrollBucketRenderer.d.ts +44 -0
- package/dist/umd/libs/visualizer/renderers/ScrollBucketRenderer.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/renderers/index.d.ts +3 -0
- package/dist/umd/libs/visualizer/renderers/index.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/shadow-dom/extractor.d.ts +23 -0
- package/dist/umd/libs/visualizer/shadow-dom/extractor.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/types/data.d.ts +72 -0
- package/dist/umd/libs/visualizer/types/data.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/{types.d.ts → types/extend.d.ts} +7 -2
- package/dist/umd/libs/visualizer/types/extend.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/types/index.d.ts +6 -0
- package/dist/umd/libs/visualizer/types/index.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/types/layout.d.ts +69 -0
- package/dist/umd/libs/visualizer/types/layout.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/types/visualize.d.ts +26 -0
- package/dist/umd/libs/visualizer/types/visualize.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/utils/delay.d.ts +21 -0
- package/dist/umd/libs/visualizer/utils/delay.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/utils/render.d.ts +7 -0
- package/dist/umd/libs/visualizer/utils/render.d.ts.map +1 -0
- package/dist/umd/stores/config.d.ts +4 -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/types/clarity.d.ts +18 -0
- package/dist/umd/types/clarity.d.ts.map +1 -1
- package/dist/umd/types/control.d.ts +2 -0
- package/dist/umd/types/control.d.ts.map +1 -1
- package/dist/umd/types/heatmap-info.d.ts +1 -0
- package/dist/umd/types/heatmap-info.d.ts.map +1 -1
- package/dist/umd/types/heatmap.d.ts +9 -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/dist/umd/types/viz-scrollmap.d.ts +4 -0
- package/dist/umd/types/viz-scrollmap.d.ts.map +1 -1
- package/dist/umd/utils/retry.d.ts +1 -0
- package/dist/umd/utils/retry.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/esm/hooks/common/useHeatmapWidthByDevice.d.ts +0 -2
- package/dist/esm/hooks/common/useHeatmapWidthByDevice.d.ts.map +0 -1
- package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts +0 -6
- package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +0 -1
- package/dist/esm/hooks/viz-scale/useContentDimensions.d.ts +0 -9
- package/dist/esm/hooks/viz-scale/useContentDimensions.d.ts.map +0 -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/esm/libs/visualizer/AttentionMapRenderer.d.ts.map +0 -1
- package/dist/esm/libs/visualizer/ClickHeatmapRenderer.d.ts +0 -30
- package/dist/esm/libs/visualizer/ClickHeatmapRenderer.d.ts.map +0 -1
- package/dist/esm/libs/visualizer/ScrollHeatmapRenderer.d.ts +0 -17
- package/dist/esm/libs/visualizer/ScrollHeatmapRenderer.d.ts.map +0 -1
- package/dist/esm/libs/visualizer/types.d.ts.map +0 -1
- package/dist/umd/hooks/common/useHeatmapWidthByDevice.d.ts +0 -2
- package/dist/umd/hooks/common/useHeatmapWidthByDevice.d.ts.map +0 -1
- package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts +0 -6
- package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +0 -1
- package/dist/umd/hooks/viz-scale/useContentDimensions.d.ts +0 -9
- package/dist/umd/hooks/viz-scale/useContentDimensions.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/umd/libs/visualizer/AttentionMapRenderer.d.ts.map +0 -1
- package/dist/umd/libs/visualizer/ClickHeatmapRenderer.d.ts +0 -30
- package/dist/umd/libs/visualizer/ClickHeatmapRenderer.d.ts.map +0 -1
- package/dist/umd/libs/visualizer/ScrollHeatmapRenderer.d.ts +0 -17
- package/dist/umd/libs/visualizer/ScrollHeatmapRenderer.d.ts.map +0 -1
- package/dist/umd/libs/visualizer/types.d.ts.map +0 -1
package/dist/esm/index.js
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";
|
|
@@ -161,6 +162,11 @@ var EHeatmapType;
|
|
|
161
162
|
EHeatmapType["Click"] = "click";
|
|
162
163
|
EHeatmapType["Scroll"] = "scroll";
|
|
163
164
|
})(EHeatmapType || (EHeatmapType = {}));
|
|
165
|
+
var EHeatmapMode;
|
|
166
|
+
(function (EHeatmapMode) {
|
|
167
|
+
EHeatmapMode["Heatmap"] = "heatmap";
|
|
168
|
+
EHeatmapMode["Replay"] = "replay";
|
|
169
|
+
})(EHeatmapMode || (EHeatmapMode = {}));
|
|
164
170
|
var EClickType;
|
|
165
171
|
(function (EClickType) {
|
|
166
172
|
/** Return all clicks (default) */
|
|
@@ -197,12 +203,16 @@ var EScrollType;
|
|
|
197
203
|
EScrollType["Attention"] = "ATTENTION";
|
|
198
204
|
EScrollType["Revenue"] = "REVENUE";
|
|
199
205
|
})(EScrollType || (EScrollType = {}));
|
|
200
|
-
var EHeatmapMode;
|
|
201
206
|
(function (EHeatmapMode) {
|
|
202
207
|
EHeatmapMode["Single"] = "single";
|
|
203
208
|
EHeatmapMode["Live"] = "live";
|
|
204
209
|
EHeatmapMode["Compare"] = "compare";
|
|
205
210
|
})(EHeatmapMode || (EHeatmapMode = {}));
|
|
211
|
+
var EHeatmapDataSource;
|
|
212
|
+
(function (EHeatmapDataSource) {
|
|
213
|
+
EHeatmapDataSource["Snapshot"] = "snapshot";
|
|
214
|
+
EHeatmapDataSource["Live"] = "live";
|
|
215
|
+
})(EHeatmapDataSource || (EHeatmapDataSource = {}));
|
|
206
216
|
|
|
207
217
|
const ViewIdContext = createContext(undefined);
|
|
208
218
|
const useViewIdContext = () => {
|
|
@@ -314,18 +324,21 @@ const useHeatmapConfigStore = create()((set) => {
|
|
|
314
324
|
return {
|
|
315
325
|
mode: EHeatmapMode.Single,
|
|
316
326
|
sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
|
|
317
|
-
|
|
318
|
-
|
|
327
|
+
shopId: undefined,
|
|
328
|
+
excludeClassNames: [],
|
|
319
329
|
setMode: (mode) => set({ mode }),
|
|
320
330
|
resetMode: () => set({ mode: EHeatmapMode.Single }),
|
|
321
331
|
setSidebarWidth: (sidebarWidth) => set({ sidebarWidth }),
|
|
322
|
-
|
|
332
|
+
setShopId: (shopId) => set({ shopId }),
|
|
333
|
+
setExcludeClassNames: (excludeClassNames) => set({ excludeClassNames }),
|
|
323
334
|
};
|
|
324
335
|
});
|
|
325
336
|
|
|
326
337
|
const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
327
338
|
return {
|
|
328
339
|
data: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
340
|
+
dataHash: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
341
|
+
dataSnapshot: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
329
342
|
clickmap: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
330
343
|
clickAreas: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
331
344
|
dataInfo: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
@@ -358,6 +371,16 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
358
371
|
newData.set(viewId, data);
|
|
359
372
|
return { data: newData };
|
|
360
373
|
}),
|
|
374
|
+
setDataHash: (dataHash, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
375
|
+
const newDataHash = new Map(prev.dataHash);
|
|
376
|
+
newDataHash.set(viewId, dataHash);
|
|
377
|
+
return { dataHash: newDataHash };
|
|
378
|
+
}),
|
|
379
|
+
setDataSnapshot: (data, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
380
|
+
const newDataSnapshot = new Map(prev.dataSnapshot);
|
|
381
|
+
newDataSnapshot.set(viewId, data);
|
|
382
|
+
return { dataSnapshot: newDataSnapshot };
|
|
383
|
+
}),
|
|
361
384
|
setClickmap: (clickmap, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
362
385
|
const newClickmap = new Map(prev.clickmap);
|
|
363
386
|
newClickmap.set(viewId, clickmap);
|
|
@@ -380,12 +403,16 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
380
403
|
}),
|
|
381
404
|
copyView: (fromViewId, toViewId) => set((prev) => {
|
|
382
405
|
const newData = new Map(prev.data);
|
|
406
|
+
const newDataSnapshot = new Map(prev.dataSnapshot);
|
|
383
407
|
const newClickmap = new Map(prev.clickmap);
|
|
384
408
|
const newClickAreas = new Map(prev.clickAreas);
|
|
385
409
|
const newDataInfo = new Map(prev.dataInfo);
|
|
386
410
|
const newScrollmap = new Map(prev.scrollmap);
|
|
387
411
|
const newAttentionMap = new Map(prev.attentionMap);
|
|
412
|
+
const newDataHash = new Map(prev.dataHash);
|
|
388
413
|
newData.set(toViewId, prev.data.get(fromViewId));
|
|
414
|
+
newDataSnapshot.set(toViewId, prev.dataSnapshot.get(fromViewId));
|
|
415
|
+
newDataHash.set(toViewId, prev.dataHash.get(fromViewId));
|
|
389
416
|
newClickmap.set(toViewId, prev.clickmap.get(fromViewId));
|
|
390
417
|
newClickAreas.set(toViewId, prev.clickAreas.get(fromViewId));
|
|
391
418
|
newDataInfo.set(toViewId, prev.dataInfo.get(fromViewId));
|
|
@@ -393,21 +420,27 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
393
420
|
newAttentionMap.set(toViewId, prev.attentionMap.get(fromViewId));
|
|
394
421
|
return {
|
|
395
422
|
data: newData,
|
|
423
|
+
dataSnapshot: newDataSnapshot,
|
|
396
424
|
clickmap: newClickmap,
|
|
397
425
|
clickAreas: newClickAreas,
|
|
398
426
|
dataInfo: newDataInfo,
|
|
399
427
|
scrollmap: newScrollmap,
|
|
400
428
|
attentionMap: newAttentionMap,
|
|
429
|
+
dataHash: newDataHash,
|
|
401
430
|
};
|
|
402
431
|
}),
|
|
403
432
|
clearView: (viewId) => set((prev) => {
|
|
404
433
|
const newData = new Map(prev.data);
|
|
434
|
+
const newDataSnapshot = new Map(prev.dataSnapshot);
|
|
405
435
|
const newClickmap = new Map(prev.clickmap);
|
|
406
436
|
const newClickAreas = new Map(prev.clickAreas);
|
|
407
437
|
const newDataInfo = new Map(prev.dataInfo);
|
|
408
438
|
const newScrollmap = new Map(prev.scrollmap);
|
|
409
439
|
const newAttentionMap = new Map(prev.attentionMap);
|
|
440
|
+
const newDataHash = new Map(prev.dataHash);
|
|
410
441
|
newData.delete(viewId);
|
|
442
|
+
newDataSnapshot.delete(viewId);
|
|
443
|
+
newDataHash.delete(viewId);
|
|
411
444
|
newClickmap.delete(viewId);
|
|
412
445
|
newClickAreas.delete(viewId);
|
|
413
446
|
newDataInfo.delete(viewId);
|
|
@@ -415,6 +448,8 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
415
448
|
newAttentionMap.delete(viewId);
|
|
416
449
|
return {
|
|
417
450
|
data: newData,
|
|
451
|
+
dataSnapshot: newDataSnapshot,
|
|
452
|
+
dataHash: newDataHash,
|
|
418
453
|
clickmap: newClickmap,
|
|
419
454
|
clickAreas: newClickAreas,
|
|
420
455
|
dataInfo: newDataInfo,
|
|
@@ -424,6 +459,8 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
424
459
|
}),
|
|
425
460
|
resetAll: () => set({
|
|
426
461
|
data: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
462
|
+
dataSnapshot: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
463
|
+
dataHash: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
427
464
|
clickmap: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
428
465
|
clickAreas: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
429
466
|
dataInfo: new Map([[DEFAULT_VIEW_ID, undefined]]),
|
|
@@ -445,6 +482,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
445
482
|
clickMode: new Map([[DEFAULT_VIEW_ID, EClickMode.Default]]),
|
|
446
483
|
scrollType: new Map([[DEFAULT_VIEW_ID, EScrollType.Depth]]),
|
|
447
484
|
heatmapType: new Map([[DEFAULT_VIEW_ID, EHeatmapType.Click]]),
|
|
485
|
+
dataSource: new Map([[DEFAULT_VIEW_ID, EHeatmapDataSource.Snapshot]]),
|
|
448
486
|
setIsRendering: (isRendering, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
449
487
|
const newIsRendering = new Map(prev.isRendering);
|
|
450
488
|
newIsRendering.set(viewId, isRendering);
|
|
@@ -495,6 +533,11 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
495
533
|
newHeatmapType.set(viewId, heatmapType);
|
|
496
534
|
return { heatmapType: newHeatmapType };
|
|
497
535
|
}),
|
|
536
|
+
setDataSource: (dataSource, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
537
|
+
const newDataSource = new Map(prev.dataSource);
|
|
538
|
+
newDataSource.set(viewId, dataSource);
|
|
539
|
+
return { dataSource: newDataSource };
|
|
540
|
+
}),
|
|
498
541
|
copyView: (fromViewId, toViewId) => set((prev) => {
|
|
499
542
|
const newIsLoadingDom = new Map(prev.isLoadingDom);
|
|
500
543
|
const newIsLoadingCanvas = new Map(prev.isLoadingCanvas);
|
|
@@ -505,6 +548,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
505
548
|
const newClickMode = new Map(prev.clickMode);
|
|
506
549
|
const newScrollType = new Map(prev.scrollType);
|
|
507
550
|
const newHeatmapType = new Map(prev.heatmapType);
|
|
551
|
+
const newDataSource = new Map(prev.dataSource);
|
|
508
552
|
newIsShowSidebar.set(toViewId, prev.isShowSidebar.get(fromViewId) ?? false);
|
|
509
553
|
newRankedBy.set(toViewId, prev.rankedBy.get(fromViewId));
|
|
510
554
|
newDeviceType.set(toViewId, prev.deviceType.get(fromViewId));
|
|
@@ -514,6 +558,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
514
558
|
newHeatmapType.set(toViewId, prev.heatmapType.get(fromViewId));
|
|
515
559
|
newIsLoadingDom.set(toViewId, prev.isLoadingDom.get(fromViewId) ?? false);
|
|
516
560
|
newIsLoadingCanvas.set(toViewId, prev.isLoadingCanvas.get(fromViewId) ?? false);
|
|
561
|
+
newDataSource.set(toViewId, prev.dataSource.get(fromViewId) ?? EHeatmapDataSource.Snapshot);
|
|
517
562
|
return {
|
|
518
563
|
isShowSidebar: newIsShowSidebar,
|
|
519
564
|
rankedBy: newRankedBy,
|
|
@@ -524,6 +569,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
524
569
|
clickMode: newClickMode,
|
|
525
570
|
scrollType: newScrollType,
|
|
526
571
|
heatmapType: newHeatmapType,
|
|
572
|
+
dataSource: newDataSource,
|
|
527
573
|
};
|
|
528
574
|
}),
|
|
529
575
|
clearView: (viewId) => set((prev) => {
|
|
@@ -536,6 +582,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
536
582
|
const newClickMode = new Map(prev.clickMode);
|
|
537
583
|
const newScrollType = new Map(prev.scrollType);
|
|
538
584
|
const newHeatmapType = new Map(prev.heatmapType);
|
|
585
|
+
const newDataSource = new Map(prev.dataSource);
|
|
539
586
|
newIsShowSidebar.delete(viewId);
|
|
540
587
|
newRankedBy.delete(viewId);
|
|
541
588
|
newIsLoadingDom.delete(viewId);
|
|
@@ -545,6 +592,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
545
592
|
newClickMode.delete(viewId);
|
|
546
593
|
newScrollType.delete(viewId);
|
|
547
594
|
newHeatmapType.delete(viewId);
|
|
595
|
+
newDataSource.delete(viewId);
|
|
548
596
|
return {
|
|
549
597
|
isShowSidebar: newIsShowSidebar,
|
|
550
598
|
rankedBy: newRankedBy,
|
|
@@ -555,6 +603,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
555
603
|
clickMode: newClickMode,
|
|
556
604
|
scrollType: newScrollType,
|
|
557
605
|
heatmapType: newHeatmapType,
|
|
606
|
+
dataSource: newDataSource,
|
|
558
607
|
};
|
|
559
608
|
}),
|
|
560
609
|
resetAll: () => set({
|
|
@@ -568,22 +617,23 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
|
|
|
568
617
|
clickMode: new Map([[DEFAULT_VIEW_ID, EClickMode.Default]]),
|
|
569
618
|
scrollType: new Map([[DEFAULT_VIEW_ID, EScrollType.Depth]]),
|
|
570
619
|
heatmapType: new Map([[DEFAULT_VIEW_ID, EHeatmapType.Click]]),
|
|
620
|
+
dataSource: new Map([[DEFAULT_VIEW_ID, EHeatmapDataSource.Snapshot]]),
|
|
571
621
|
}),
|
|
572
622
|
};
|
|
573
623
|
}));
|
|
574
624
|
|
|
575
625
|
const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
|
|
576
626
|
return {
|
|
577
|
-
|
|
627
|
+
isDomLoaded: new Map([[DEFAULT_VIEW_ID, false]]),
|
|
578
628
|
zoomRatio: new Map([[DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO.DEFAULT]]),
|
|
579
629
|
minZoomRatio: new Map([[DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO.MIN]]),
|
|
580
630
|
maxZoomRatio: new Map([[DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO.MAX]]),
|
|
581
631
|
scale: new Map([[DEFAULT_VIEW_ID, 1]]),
|
|
582
632
|
isScaledToFit: new Map([[DEFAULT_VIEW_ID, false]]),
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
return {
|
|
633
|
+
setIsDomLoaded: (isDomLoaded, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
634
|
+
const newIsDomLoaded = new Map(prev.isDomLoaded);
|
|
635
|
+
newIsDomLoaded.set(viewId, isDomLoaded);
|
|
636
|
+
return { isDomLoaded: newIsDomLoaded };
|
|
587
637
|
}),
|
|
588
638
|
setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
589
639
|
const newZoomRatio = new Map(prev.zoomRatio);
|
|
@@ -611,18 +661,18 @@ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
|
|
|
611
661
|
return { isScaledToFit: newIsScaledToFit };
|
|
612
662
|
}),
|
|
613
663
|
copyView: (fromViewId, toViewId) => set((prev) => {
|
|
614
|
-
const
|
|
664
|
+
const newIsDomLoaded = new Map(prev.isDomLoaded);
|
|
615
665
|
const newZoomRatio = new Map(prev.zoomRatio);
|
|
616
666
|
const newMinZoomRatio = new Map(prev.minZoomRatio);
|
|
617
667
|
const newScale = new Map(prev.scale);
|
|
618
668
|
const newIsScaledToFit = new Map(prev.isScaledToFit);
|
|
619
|
-
|
|
669
|
+
newIsDomLoaded.set(toViewId, prev.isDomLoaded.get(fromViewId) ?? false);
|
|
620
670
|
newZoomRatio.set(toViewId, prev.zoomRatio.get(fromViewId) ?? 100);
|
|
621
671
|
newMinZoomRatio.set(toViewId, prev.minZoomRatio.get(fromViewId) ?? 10);
|
|
622
672
|
newScale.set(toViewId, prev.scale.get(fromViewId) ?? 1);
|
|
623
673
|
newIsScaledToFit.set(toViewId, prev.isScaledToFit.get(fromViewId) ?? false);
|
|
624
674
|
return {
|
|
625
|
-
|
|
675
|
+
isDomLoaded: newIsDomLoaded,
|
|
626
676
|
zoomRatio: newZoomRatio,
|
|
627
677
|
minZoomRatio: newMinZoomRatio,
|
|
628
678
|
scale: newScale,
|
|
@@ -630,18 +680,18 @@ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
|
|
|
630
680
|
};
|
|
631
681
|
}),
|
|
632
682
|
clearView: (viewId) => set((prev) => {
|
|
633
|
-
const
|
|
683
|
+
const newIsDomLoaded = new Map(prev.isDomLoaded);
|
|
634
684
|
const newZoomRatio = new Map(prev.zoomRatio);
|
|
635
685
|
const newMinZoomRatio = new Map(prev.minZoomRatio);
|
|
636
686
|
const newScale = new Map(prev.scale);
|
|
637
687
|
const newIsScaledToFit = new Map(prev.isScaledToFit);
|
|
638
|
-
|
|
688
|
+
newIsDomLoaded.delete(viewId);
|
|
639
689
|
newZoomRatio.delete(viewId);
|
|
640
690
|
newMinZoomRatio.delete(viewId);
|
|
641
691
|
newScale.delete(viewId);
|
|
642
692
|
newIsScaledToFit.delete(viewId);
|
|
643
693
|
return {
|
|
644
|
-
|
|
694
|
+
isDomLoaded: newIsDomLoaded,
|
|
645
695
|
zoomRatio: newZoomRatio,
|
|
646
696
|
minZoomRatio: newMinZoomRatio,
|
|
647
697
|
scale: newScale,
|
|
@@ -649,7 +699,7 @@ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
|
|
|
649
699
|
};
|
|
650
700
|
}),
|
|
651
701
|
resetAll: () => set({
|
|
652
|
-
|
|
702
|
+
isDomLoaded: new Map([[DEFAULT_VIEW_ID, false]]),
|
|
653
703
|
zoomRatio: new Map([[DEFAULT_VIEW_ID, 100]]),
|
|
654
704
|
minZoomRatio: new Map([[DEFAULT_VIEW_ID, 10]]),
|
|
655
705
|
scale: new Map([[DEFAULT_VIEW_ID, 1]]),
|
|
@@ -1047,24 +1097,108 @@ const useHeatmapCompareStore = create()((set, get) => {
|
|
|
1047
1097
|
});
|
|
1048
1098
|
|
|
1049
1099
|
const initialState = {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1100
|
+
decodedPayloads: new Map([[DEFAULT_VIEW_ID, []]]),
|
|
1101
|
+
encodedPayloads: new Map([[DEFAULT_VIEW_ID, []]]),
|
|
1102
|
+
htmlContent: new Map([[DEFAULT_VIEW_ID, '']]),
|
|
1103
|
+
targetUrl: new Map([[DEFAULT_VIEW_ID, '']]),
|
|
1104
|
+
renderMode: new Map([[DEFAULT_VIEW_ID, 'portal']]),
|
|
1105
|
+
storefrontPassword: new Map([[DEFAULT_VIEW_ID, '']]),
|
|
1106
|
+
};
|
|
1107
|
+
const useHeatmapLiveStore = create()(subscribeWithSelector((set) => ({
|
|
1108
|
+
...initialState,
|
|
1109
|
+
addPayload: (payload, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1110
|
+
const newDecoded = new Map(prev.decodedPayloads);
|
|
1111
|
+
newDecoded.set(viewId, [...(newDecoded.get(viewId) ?? []), payload]);
|
|
1112
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1113
|
+
newEncoded.set(viewId, [...(newEncoded.get(viewId) ?? []), JSON.stringify(payload)]);
|
|
1114
|
+
return { decodedPayloads: newDecoded, encodedPayloads: newEncoded };
|
|
1115
|
+
}),
|
|
1116
|
+
setPayloads: (payloads, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1117
|
+
const newDecoded = new Map(prev.decodedPayloads);
|
|
1118
|
+
newDecoded.set(viewId, payloads);
|
|
1119
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1120
|
+
newEncoded.set(viewId, payloads.map((p) => JSON.stringify(p)));
|
|
1121
|
+
return { decodedPayloads: newDecoded, encodedPayloads: newEncoded };
|
|
1122
|
+
}),
|
|
1123
|
+
setEncodedPayloads: (payloads, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1124
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1125
|
+
newEncoded.set(viewId, payloads);
|
|
1126
|
+
return { encodedPayloads: newEncoded };
|
|
1127
|
+
}),
|
|
1128
|
+
setHtmlContent: (htmlContent, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1129
|
+
const newHtmlContent = new Map(prev.htmlContent);
|
|
1130
|
+
newHtmlContent.set(viewId, htmlContent);
|
|
1131
|
+
return { htmlContent: newHtmlContent };
|
|
1132
|
+
}),
|
|
1133
|
+
setTargetUrl: (targetUrl, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1134
|
+
const newTargetUrl = new Map(prev.targetUrl);
|
|
1135
|
+
newTargetUrl.set(viewId, targetUrl);
|
|
1136
|
+
return { targetUrl: newTargetUrl };
|
|
1137
|
+
}),
|
|
1138
|
+
setRenderMode: (renderMode, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1139
|
+
const newRenderMode = new Map(prev.renderMode);
|
|
1140
|
+
newRenderMode.set(viewId, renderMode);
|
|
1141
|
+
return { renderMode: newRenderMode };
|
|
1142
|
+
}),
|
|
1143
|
+
setStorefrontPassword: (storefrontPassword, viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1144
|
+
const newStorefrontPassword = new Map(prev.storefrontPassword);
|
|
1145
|
+
newStorefrontPassword.set(viewId, storefrontPassword);
|
|
1146
|
+
return { storefrontPassword: newStorefrontPassword };
|
|
1147
|
+
}),
|
|
1148
|
+
resetView: (viewId = DEFAULT_VIEW_ID) => set((prev) => {
|
|
1149
|
+
const newDecoded = new Map(prev.decodedPayloads);
|
|
1150
|
+
newDecoded.set(viewId, []);
|
|
1151
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1152
|
+
newEncoded.set(viewId, []);
|
|
1153
|
+
return { decodedPayloads: newDecoded, encodedPayloads: newEncoded };
|
|
1154
|
+
}),
|
|
1155
|
+
copyView: (fromViewId, toViewId) => set((prev) => {
|
|
1156
|
+
const newDecoded = new Map(prev.decodedPayloads);
|
|
1157
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1158
|
+
const newHtmlContent = new Map(prev.htmlContent);
|
|
1159
|
+
const newTargetUrl = new Map(prev.targetUrl);
|
|
1160
|
+
const newRenderMode = new Map(prev.renderMode);
|
|
1161
|
+
const newStorefrontPassword = new Map(prev.storefrontPassword);
|
|
1162
|
+
newDecoded.set(toViewId, prev.decodedPayloads.get(fromViewId) ?? []);
|
|
1163
|
+
newEncoded.set(toViewId, prev.encodedPayloads.get(fromViewId) ?? []);
|
|
1164
|
+
newHtmlContent.set(toViewId, prev.htmlContent.get(fromViewId) ?? '');
|
|
1165
|
+
newTargetUrl.set(toViewId, prev.targetUrl.get(fromViewId) ?? '');
|
|
1166
|
+
newRenderMode.set(toViewId, prev.renderMode.get(fromViewId) ?? 'portal');
|
|
1167
|
+
newStorefrontPassword.set(toViewId, prev.storefrontPassword.get(fromViewId) ?? '');
|
|
1168
|
+
return {
|
|
1169
|
+
decodedPayloads: newDecoded,
|
|
1170
|
+
encodedPayloads: newEncoded,
|
|
1171
|
+
htmlContent: newHtmlContent,
|
|
1172
|
+
targetUrl: newTargetUrl,
|
|
1173
|
+
renderMode: newRenderMode,
|
|
1174
|
+
storefrontPassword: newStorefrontPassword,
|
|
1175
|
+
};
|
|
1176
|
+
}),
|
|
1177
|
+
clearView: (viewId) => set((prev) => {
|
|
1178
|
+
const newDecoded = new Map(prev.decodedPayloads);
|
|
1179
|
+
const newEncoded = new Map(prev.encodedPayloads);
|
|
1180
|
+
const newHtmlContent = new Map(prev.htmlContent);
|
|
1181
|
+
const newTargetUrl = new Map(prev.targetUrl);
|
|
1182
|
+
const newRenderMode = new Map(prev.renderMode);
|
|
1183
|
+
const newStorefrontPassword = new Map(prev.storefrontPassword);
|
|
1184
|
+
newDecoded.delete(viewId);
|
|
1185
|
+
newEncoded.delete(viewId);
|
|
1186
|
+
newHtmlContent.delete(viewId);
|
|
1187
|
+
newTargetUrl.delete(viewId);
|
|
1188
|
+
newRenderMode.delete(viewId);
|
|
1189
|
+
newStorefrontPassword.delete(viewId);
|
|
1190
|
+
return {
|
|
1191
|
+
decodedPayloads: newDecoded,
|
|
1192
|
+
encodedPayloads: newEncoded,
|
|
1193
|
+
htmlContent: newHtmlContent,
|
|
1194
|
+
targetUrl: newTargetUrl,
|
|
1195
|
+
renderMode: newRenderMode,
|
|
1196
|
+
storefrontPassword: newStorefrontPassword,
|
|
1197
|
+
};
|
|
1198
|
+
}),
|
|
1199
|
+
resetAll: () => set(initialState),
|
|
1200
|
+
reset: () => set(initialState),
|
|
1201
|
+
})));
|
|
1068
1202
|
|
|
1069
1203
|
const useHeatmapVizRectStore = create()(subscribeWithSelector((set) => {
|
|
1070
1204
|
return {
|
|
@@ -1170,26 +1304,12 @@ const useHeatmapClickContext = createViewContextHook({
|
|
|
1170
1304
|
}),
|
|
1171
1305
|
});
|
|
1172
1306
|
|
|
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
1307
|
const useHeatmapDataContext = createViewContextHook({
|
|
1190
1308
|
useStore: useHeatmapDataStore,
|
|
1191
1309
|
getState: (store, viewId) => ({
|
|
1192
1310
|
data: store.data.get(viewId),
|
|
1311
|
+
dataHash: store.dataHash.get(viewId),
|
|
1312
|
+
dataSnapshot: store.dataSnapshot.get(viewId),
|
|
1193
1313
|
clickmap: store.clickmap.get(viewId),
|
|
1194
1314
|
clickAreas: store.clickAreas.get(viewId),
|
|
1195
1315
|
scrollmap: store.scrollmap.get(viewId),
|
|
@@ -1199,6 +1319,8 @@ const useHeatmapDataContext = createViewContextHook({
|
|
|
1199
1319
|
}),
|
|
1200
1320
|
getActions: (store, viewId) => ({
|
|
1201
1321
|
setData: (newData) => store.setData(newData, viewId),
|
|
1322
|
+
setDataSnapshot: (newData) => store.setDataSnapshot(newData, viewId),
|
|
1323
|
+
setDataHash: (newHash) => store.setDataHash(newHash, viewId),
|
|
1202
1324
|
setClickmap: (newClickmap) => store.setClickmap(newClickmap, viewId),
|
|
1203
1325
|
setClickAreas: (newClickAreas) => store.setClickAreas(newClickAreas, viewId),
|
|
1204
1326
|
setDataInfoByKey: (key, value) => store.setDataInfoByKey(key, value, viewId),
|
|
@@ -1221,6 +1343,32 @@ const useHeatmapHoverContext = createViewContextHook({
|
|
|
1221
1343
|
}),
|
|
1222
1344
|
});
|
|
1223
1345
|
|
|
1346
|
+
const useHeatmapLiveContext = createViewContextHook({
|
|
1347
|
+
useStore: useHeatmapLiveStore,
|
|
1348
|
+
getState: (store, viewId) => ({
|
|
1349
|
+
decodedPayloads: store.decodedPayloads.get(viewId) ?? [],
|
|
1350
|
+
encodedPayloads: store.encodedPayloads.get(viewId) ?? [],
|
|
1351
|
+
htmlContent: store.htmlContent.get(viewId) ?? '',
|
|
1352
|
+
targetUrl: store.targetUrl.get(viewId) ?? '',
|
|
1353
|
+
renderMode: store.renderMode.get(viewId) ?? 'portal',
|
|
1354
|
+
storefrontPassword: store.storefrontPassword.get(viewId) ?? '',
|
|
1355
|
+
}),
|
|
1356
|
+
getActions: (store, viewId) => ({
|
|
1357
|
+
addPayload: (payload) => store.addPayload(payload, viewId),
|
|
1358
|
+
setPayloads: (payloads) => store.setPayloads(payloads, viewId),
|
|
1359
|
+
setEncodedPayloads: (payloads) => store.setEncodedPayloads(payloads, viewId),
|
|
1360
|
+
setHtmlContent: (htmlContent) => store.setHtmlContent(htmlContent, viewId),
|
|
1361
|
+
setTargetUrl: (targetUrl) => store.setTargetUrl(targetUrl, viewId),
|
|
1362
|
+
setRenderMode: (mode) => store.setRenderMode(mode, viewId),
|
|
1363
|
+
setStorefrontPassword: (password) => store.setStorefrontPassword(password, viewId),
|
|
1364
|
+
resetView: () => store.resetView(viewId),
|
|
1365
|
+
copyView: (fromViewId, toViewId) => store.copyView(fromViewId, toViewId),
|
|
1366
|
+
clearView: (viewId) => store.clearView(viewId),
|
|
1367
|
+
resetAll: () => store.resetAll(),
|
|
1368
|
+
reset: () => store.reset(),
|
|
1369
|
+
}),
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1224
1372
|
const useHeatmapScrollContext = createViewContextHook({
|
|
1225
1373
|
useStore: useHeatmapVizScrollStore,
|
|
1226
1374
|
getState: (store, viewId) => ({
|
|
@@ -1250,6 +1398,7 @@ const useHeatmapSettingContext = createViewContextHook({
|
|
|
1250
1398
|
clickMode: store.clickMode.get(viewId),
|
|
1251
1399
|
scrollType: store.scrollType.get(viewId),
|
|
1252
1400
|
heatmapType: store.heatmapType.get(viewId),
|
|
1401
|
+
dataSource: store.dataSource.get(viewId) ?? EHeatmapDataSource.Snapshot,
|
|
1253
1402
|
}),
|
|
1254
1403
|
getActions: (store, viewId) => ({
|
|
1255
1404
|
setIsShowSidebar: (isShowSidebar) => store.setIsShowSidebar(isShowSidebar, viewId),
|
|
@@ -1262,6 +1411,7 @@ const useHeatmapSettingContext = createViewContextHook({
|
|
|
1262
1411
|
setIsRendering: (isRendering) => store.setIsRendering(isRendering, viewId),
|
|
1263
1412
|
setIsLoadingDom: (isLoadingDom) => store.setIsLoadingDom(isLoadingDom, viewId),
|
|
1264
1413
|
setIsLoadingCanvas: (isLoadingCanvas) => store.setIsLoadingCanvas(isLoadingCanvas, viewId),
|
|
1414
|
+
setDataSource: (dataSource) => store.setDataSource(dataSource, viewId),
|
|
1265
1415
|
clearView: (viewId) => store.clearView(viewId),
|
|
1266
1416
|
}),
|
|
1267
1417
|
});
|
|
@@ -1269,7 +1419,7 @@ const useHeatmapSettingContext = createViewContextHook({
|
|
|
1269
1419
|
const useHeatmapVizContext = createViewContextHook({
|
|
1270
1420
|
useStore: useHeatmapVizStore,
|
|
1271
1421
|
getState: (store, viewId) => ({
|
|
1272
|
-
|
|
1422
|
+
isDomLoaded: store.isDomLoaded.get(viewId) ?? false,
|
|
1273
1423
|
zoomRatio: store.zoomRatio.get(viewId) ?? DEFAULT_ZOOM_RATIO.DEFAULT,
|
|
1274
1424
|
minZoomRatio: store.minZoomRatio.get(viewId) ?? DEFAULT_ZOOM_RATIO.MIN,
|
|
1275
1425
|
maxZoomRatio: store.maxZoomRatio.get(viewId) ?? DEFAULT_ZOOM_RATIO.MAX,
|
|
@@ -1277,7 +1427,7 @@ const useHeatmapVizContext = createViewContextHook({
|
|
|
1277
1427
|
isScaledToFit: store.isScaledToFit.get(viewId) ?? false,
|
|
1278
1428
|
}),
|
|
1279
1429
|
getActions: (store, viewId) => ({
|
|
1280
|
-
|
|
1430
|
+
setIsDomLoaded: (value) => store.setIsDomLoaded(value, viewId),
|
|
1281
1431
|
setZoomRatio: (value) => store.setZoomRatio(value, viewId),
|
|
1282
1432
|
setMinZoomRatio: (value) => store.setMinZoomRatio(value, viewId),
|
|
1283
1433
|
setMaxZoomRatio: (value) => store.setMaxZoomRatio(value, viewId),
|
|
@@ -1311,9 +1461,7 @@ const useHeatmapCopyView = () => {
|
|
|
1311
1461
|
const copyVizView = useHeatmapVizStore((state) => state.copyView);
|
|
1312
1462
|
const copyVizClickView = useHeatmapVizClickStore((state) => state.copyView);
|
|
1313
1463
|
const copyVizAreaClickView = useHeatmapVizClickAreaStore((state) => state.copyView);
|
|
1314
|
-
|
|
1315
|
-
// const copyVizHoverView = useHeatmapVizHoverStore((state) => state.copyView);
|
|
1316
|
-
// const copyVizScrollView = useHeatmapVizScrollStore((state) => state.copyView);
|
|
1464
|
+
const copyLiveView = useHeatmapLiveStore((state) => state.copyView);
|
|
1317
1465
|
const clearDataView = useHeatmapDataStore((state) => state.clearView);
|
|
1318
1466
|
const clearSettingView = useHeatmapSettingStore((state) => state.clearView);
|
|
1319
1467
|
const clearVizView = useHeatmapVizStore((state) => state.clearView);
|
|
@@ -1322,6 +1470,7 @@ const useHeatmapCopyView = () => {
|
|
|
1322
1470
|
const clearVizHoverView = useHeatmapVizHoverStore((state) => state.clearView);
|
|
1323
1471
|
const clearVizScrollView = useHeatmapVizScrollStore((state) => state.clearView);
|
|
1324
1472
|
const clearVizAreaClickView = useHeatmapVizClickAreaStore((state) => state.clearView);
|
|
1473
|
+
const clearLiveView = useHeatmapLiveStore((state) => state.clearView);
|
|
1325
1474
|
const resetDataAll = useHeatmapDataStore((state) => state.resetAll);
|
|
1326
1475
|
const resetSettingAll = useHeatmapSettingStore((state) => state.resetAll);
|
|
1327
1476
|
const resetVizAll = useHeatmapVizStore((state) => state.resetAll);
|
|
@@ -1330,15 +1479,14 @@ const useHeatmapCopyView = () => {
|
|
|
1330
1479
|
const resetVizHoverAll = useHeatmapVizHoverStore((state) => state.resetAll);
|
|
1331
1480
|
const resetVizScrollViewAll = useHeatmapVizScrollStore((state) => state.resetAll);
|
|
1332
1481
|
const resetVizClickAreaAll = useHeatmapVizClickAreaStore((state) => state.resetAll);
|
|
1482
|
+
const resetLiveAll = useHeatmapLiveStore((state) => state.resetAll);
|
|
1333
1483
|
const copyView = (fromViewId, toViewId) => {
|
|
1334
1484
|
copyDataView(fromViewId, toViewId);
|
|
1335
1485
|
copySettingView(fromViewId, toViewId);
|
|
1336
1486
|
copyVizView(fromViewId, toViewId);
|
|
1337
|
-
// copyVizRectView(fromViewId, toViewId);
|
|
1338
1487
|
copyVizClickView(fromViewId, toViewId);
|
|
1339
|
-
// copyVizHoverView(fromViewId, toViewId);
|
|
1340
|
-
// copyVizScrollView(fromViewId, toViewId);
|
|
1341
1488
|
copyVizAreaClickView(fromViewId, toViewId);
|
|
1489
|
+
copyLiveView(fromViewId, toViewId);
|
|
1342
1490
|
};
|
|
1343
1491
|
const copyViewToMultiple = (fromViewId, toViewIds) => {
|
|
1344
1492
|
toViewIds.forEach((toViewId) => {
|
|
@@ -1354,6 +1502,7 @@ const useHeatmapCopyView = () => {
|
|
|
1354
1502
|
clearVizHoverView(viewId);
|
|
1355
1503
|
clearVizScrollView(viewId);
|
|
1356
1504
|
clearVizAreaClickView(viewId);
|
|
1505
|
+
clearLiveView(viewId);
|
|
1357
1506
|
};
|
|
1358
1507
|
const clearMultipleViews = (viewIds) => {
|
|
1359
1508
|
viewIds.forEach((viewId) => {
|
|
@@ -1369,6 +1518,7 @@ const useHeatmapCopyView = () => {
|
|
|
1369
1518
|
resetVizHoverAll();
|
|
1370
1519
|
resetVizScrollViewAll();
|
|
1371
1520
|
resetVizClickAreaAll();
|
|
1521
|
+
resetLiveAll();
|
|
1372
1522
|
};
|
|
1373
1523
|
return {
|
|
1374
1524
|
copyView,
|
|
@@ -1379,14 +1529,17 @@ const useHeatmapCopyView = () => {
|
|
|
1379
1529
|
};
|
|
1380
1530
|
};
|
|
1381
1531
|
|
|
1382
|
-
const
|
|
1532
|
+
const useHeatmapViewportByDevice = () => {
|
|
1383
1533
|
const deviceType = useHeatmapSettingContext((state) => state.deviceType);
|
|
1384
1534
|
const width = useMemo(() => calcWidthByDeviceType(deviceType), [deviceType]);
|
|
1385
|
-
|
|
1535
|
+
const height = useMemo(() => calcHeightByDeviceType(deviceType), [deviceType]);
|
|
1536
|
+
return { width, height };
|
|
1386
1537
|
function calcWidthByDeviceType(deviceType) {
|
|
1387
1538
|
if (!deviceType)
|
|
1388
1539
|
return 1440;
|
|
1389
1540
|
switch (deviceType) {
|
|
1541
|
+
case EDeviceType.DesktopLarge:
|
|
1542
|
+
return 1920;
|
|
1390
1543
|
case EDeviceType.Desktop:
|
|
1391
1544
|
return 1440;
|
|
1392
1545
|
case EDeviceType.Tablet:
|
|
@@ -1395,20 +1548,45 @@ const useHeatmapWidthByDevice = () => {
|
|
|
1395
1548
|
return 375;
|
|
1396
1549
|
}
|
|
1397
1550
|
}
|
|
1551
|
+
function calcHeightByDeviceType(deviceType) {
|
|
1552
|
+
if (!deviceType)
|
|
1553
|
+
return 900;
|
|
1554
|
+
switch (deviceType) {
|
|
1555
|
+
case EDeviceType.DesktopLarge:
|
|
1556
|
+
return 1080; // 1920×1080
|
|
1557
|
+
case EDeviceType.Desktop:
|
|
1558
|
+
return 900; // 1440×900
|
|
1559
|
+
case EDeviceType.Tablet:
|
|
1560
|
+
return 1024; // 768×1024 (iPad portrait)
|
|
1561
|
+
case EDeviceType.Mobile:
|
|
1562
|
+
return 812; // 375×812 (iPhone X/11/12/13)
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1398
1565
|
};
|
|
1399
1566
|
|
|
1400
|
-
const useRegisterConfig = ({ isLoading, isLoadingCanvas, }) => {
|
|
1567
|
+
const useRegisterConfig = ({ shopId, isLoading, isLoadingCanvas, excludeClassNames, }) => {
|
|
1401
1568
|
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
1569
|
+
const shopIdStore = useHeatmapConfigStore((state) => state.shopId);
|
|
1402
1570
|
const deviceType = useHeatmapSettingContext((state) => state.deviceType);
|
|
1403
1571
|
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
1404
1572
|
const setIsRendering = useHeatmapSettingContext((state) => state.setIsRendering);
|
|
1405
1573
|
const setIsLoadingDom = useHeatmapSettingContext((state) => state.setIsLoadingDom);
|
|
1406
1574
|
const setIsLoadingCanvas = useHeatmapSettingContext((state) => state.setIsLoadingCanvas);
|
|
1575
|
+
const setShopId = useHeatmapConfigStore((state) => state.setShopId);
|
|
1576
|
+
const setExcludeClassNames = useHeatmapConfigStore((state) => state.setExcludeClassNames);
|
|
1577
|
+
useEffect(() => {
|
|
1578
|
+
if (!shopId || !!shopIdStore || shopIdStore === shopId)
|
|
1579
|
+
return;
|
|
1580
|
+
setShopId(shopId);
|
|
1581
|
+
}, [shopId, setShopId, shopIdStore]);
|
|
1582
|
+
useEffect(() => {
|
|
1583
|
+
setExcludeClassNames(excludeClassNames || []);
|
|
1584
|
+
}, [excludeClassNames, setExcludeClassNames]);
|
|
1407
1585
|
useEffect(() => {
|
|
1408
1586
|
setIsRendering(true);
|
|
1409
1587
|
setTimeout(() => {
|
|
1410
1588
|
setIsRendering(false);
|
|
1411
|
-
},
|
|
1589
|
+
}, 500);
|
|
1412
1590
|
}, [mode, deviceType, sidebarWidth]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
1413
1591
|
useEffect(() => {
|
|
1414
1592
|
setIsRendering(!!isLoading);
|
|
@@ -1770,7 +1948,7 @@ class Logger {
|
|
|
1770
1948
|
}
|
|
1771
1949
|
}
|
|
1772
1950
|
// Export singleton instance
|
|
1773
|
-
const logger$
|
|
1951
|
+
const logger$3 = new Logger();
|
|
1774
1952
|
// Export factory function để tạo logger với config riêng
|
|
1775
1953
|
function createLogger(config = {}) {
|
|
1776
1954
|
const instance = new Logger();
|
|
@@ -2159,7 +2337,7 @@ function findElementByHash(props) {
|
|
|
2159
2337
|
}
|
|
2160
2338
|
}
|
|
2161
2339
|
catch (error) {
|
|
2162
|
-
logger$
|
|
2340
|
+
logger$3.warn(`Invalid selector "${selector}":`, error);
|
|
2163
2341
|
}
|
|
2164
2342
|
const elementByHash = iframeDocument.querySelector(`[data-clarity-hashalpha="${hash}"], [data-clarity-hash="${hash}"], [data-clarity-hashbeta="${hash}"]`);
|
|
2165
2343
|
return elementByHash;
|
|
@@ -2181,7 +2359,7 @@ function hydrateAreaNode(props) {
|
|
|
2181
2359
|
const { id, hash, selector } = persistedData;
|
|
2182
2360
|
const element = findElementByHash({ hash, selector, iframeDocument, vizRef });
|
|
2183
2361
|
if (!element) {
|
|
2184
|
-
logger$
|
|
2362
|
+
logger$3.warn(`Cannot hydrate area ${id}: element not found for hash ${hash} or selector ${selector}`);
|
|
2185
2363
|
return null;
|
|
2186
2364
|
}
|
|
2187
2365
|
const areaNode = buildAreaNode(element, hash, heatmapInfo, shadowRoot, persistedData);
|
|
@@ -2198,7 +2376,7 @@ function hydrateAreas(props) {
|
|
|
2198
2376
|
hydratedAreas.push(area);
|
|
2199
2377
|
}
|
|
2200
2378
|
}
|
|
2201
|
-
logger$
|
|
2379
|
+
logger$3.info(`Hydrated ${hydratedAreas.length} of ${clickAreas.length} persisted areas`);
|
|
2202
2380
|
return hydratedAreas;
|
|
2203
2381
|
}
|
|
2204
2382
|
/**
|
|
@@ -2902,16 +3080,16 @@ const calcCalloutPositionAbsolute = (props) => {
|
|
|
2902
3080
|
|
|
2903
3081
|
function validateAreaCreation(dataInfo, hash, areas) {
|
|
2904
3082
|
if (!dataInfo?.clickMapMetrics || !dataInfo?.totalClicks) {
|
|
2905
|
-
logger$
|
|
3083
|
+
logger$3.warn('Cannot create area: missing heatmap data');
|
|
2906
3084
|
return false;
|
|
2907
3085
|
}
|
|
2908
3086
|
if (!hash) {
|
|
2909
|
-
logger$
|
|
3087
|
+
logger$3.warn('Cannot create area: missing hash');
|
|
2910
3088
|
return false;
|
|
2911
3089
|
}
|
|
2912
3090
|
const alreadyExists = areas.some((area) => area.hash === hash);
|
|
2913
3091
|
if (alreadyExists) {
|
|
2914
|
-
logger$
|
|
3092
|
+
logger$3.warn(`Area already exists for element: ${hash}`);
|
|
2915
3093
|
return false;
|
|
2916
3094
|
}
|
|
2917
3095
|
return true;
|
|
@@ -2924,14 +3102,14 @@ function identifyConflictingAreas(area) {
|
|
|
2924
3102
|
// Case 1: New area is a child of an existing area
|
|
2925
3103
|
if (area.parentNode) {
|
|
2926
3104
|
conflicts.parentId = area.parentNode.id;
|
|
2927
|
-
logger$
|
|
3105
|
+
logger$3.info(`New area "${area.selector}" is a child of existing area "${area.parentNode.selector}". Will remove parent.`);
|
|
2928
3106
|
}
|
|
2929
3107
|
// Case 2: New area is a parent of existing area(s)
|
|
2930
3108
|
if (area.childNodes.size > 0) {
|
|
2931
3109
|
area.childNodes.forEach((childArea) => {
|
|
2932
3110
|
conflicts.childrenIds.push(childArea.id);
|
|
2933
3111
|
});
|
|
2934
|
-
logger$
|
|
3112
|
+
logger$3.info(`New area "${area.selector}" is a parent of ${area.childNodes.size} existing area(s). Will remove children.`);
|
|
2935
3113
|
}
|
|
2936
3114
|
return conflicts;
|
|
2937
3115
|
}
|
|
@@ -2982,7 +3160,7 @@ function useAreaCreation(options = {}) {
|
|
|
2982
3160
|
}
|
|
2983
3161
|
}
|
|
2984
3162
|
catch (error) {
|
|
2985
|
-
logger$
|
|
3163
|
+
logger$3.error('Failed to create area:', error);
|
|
2986
3164
|
}
|
|
2987
3165
|
}, [dataInfo, areas, addArea, removeArea, removeClickArea, customShadowRoot, onAreaCreated]);
|
|
2988
3166
|
return {
|
|
@@ -3097,16 +3275,16 @@ function useAreaHydration(options) {
|
|
|
3097
3275
|
return;
|
|
3098
3276
|
if (!dataInfo)
|
|
3099
3277
|
return;
|
|
3100
|
-
logger$
|
|
3278
|
+
logger$3.info(`Hydrating ${clickAreas.length} persisted areas...`);
|
|
3101
3279
|
const hydratedAreas = hydrateAreas({ clickAreas, heatmapInfo: dataInfo, vizRef, shadowRoot });
|
|
3102
3280
|
if (!hydratedAreas?.length) {
|
|
3103
|
-
logger$
|
|
3281
|
+
logger$3.warn('No areas could be hydrated - all elements may have been removed from DOM');
|
|
3104
3282
|
return;
|
|
3105
3283
|
}
|
|
3106
3284
|
setIsInitializing(true);
|
|
3107
3285
|
buildAreaGraph(hydratedAreas);
|
|
3108
3286
|
setAreas(hydratedAreas);
|
|
3109
|
-
logger$
|
|
3287
|
+
logger$3.info(`Successfully hydrated ${hydratedAreas.length} areas`);
|
|
3110
3288
|
}, [dataInfo, vizRef, isInitializing, clickAreas]);
|
|
3111
3289
|
useEffect(() => {
|
|
3112
3290
|
if (!enabled)
|
|
@@ -3240,7 +3418,7 @@ function useAreaRectSync(options) {
|
|
|
3240
3418
|
area.rect.update(newRect);
|
|
3241
3419
|
}
|
|
3242
3420
|
catch (error) {
|
|
3243
|
-
logger$
|
|
3421
|
+
logger$3.error(`Failed to update rect for area ${area.id}:`, error);
|
|
3244
3422
|
}
|
|
3245
3423
|
});
|
|
3246
3424
|
buildAreaGraph(areas);
|
|
@@ -3345,18 +3523,18 @@ const useAreaClickmap = () => {
|
|
|
3345
3523
|
const useClickmap = () => {
|
|
3346
3524
|
const vizRef = useHeatmapVizRectContext((s) => s.vizRef);
|
|
3347
3525
|
const clickmap = useHeatmapDataContext((s) => s.clickmap);
|
|
3348
|
-
const
|
|
3526
|
+
const isDomLoaded = useHeatmapVizContext((s) => s.isDomLoaded);
|
|
3349
3527
|
const start = useCallback(() => {
|
|
3350
|
-
if (!vizRef || !clickmap || clickmap.length === 0 || !
|
|
3528
|
+
if (!vizRef || !clickmap || clickmap.length === 0 || !isDomLoaded)
|
|
3351
3529
|
return;
|
|
3352
3530
|
try {
|
|
3353
3531
|
vizRef?.clearmap?.();
|
|
3354
|
-
vizRef?.clickmap?.(clickmap);
|
|
3532
|
+
requestIdleCallback(() => vizRef?.clickmap?.(clickmap), { timeout: 300 });
|
|
3355
3533
|
}
|
|
3356
3534
|
catch (error) {
|
|
3357
3535
|
console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
|
|
3358
3536
|
}
|
|
3359
|
-
}, [vizRef, clickmap,
|
|
3537
|
+
}, [vizRef, clickmap, isDomLoaded]);
|
|
3360
3538
|
return { start };
|
|
3361
3539
|
};
|
|
3362
3540
|
|
|
@@ -3364,29 +3542,48 @@ const useScrollmap = () => {
|
|
|
3364
3542
|
const vizRef = useHeatmapVizRectContext((s) => s.vizRef);
|
|
3365
3543
|
const scrollType = useHeatmapSettingContext((s) => s.scrollType);
|
|
3366
3544
|
const scrollmap = useHeatmapDataContext((s) => s.scrollmap);
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
case EScrollType.Revenue:
|
|
3374
|
-
return scrollmap;
|
|
3375
|
-
default:
|
|
3376
|
-
return scrollmap;
|
|
3545
|
+
const isDomLoaded = useHeatmapVizContext((s) => s.isDomLoaded);
|
|
3546
|
+
const renderScrollmap = useCallback(() => {
|
|
3547
|
+
if (!vizRef || !scrollmap || scrollmap.length === 0 || !isDomLoaded)
|
|
3548
|
+
return;
|
|
3549
|
+
try {
|
|
3550
|
+
requestIdleCallback(() => vizRef?.scrollmap?.(scrollmap), { timeout: 300 });
|
|
3377
3551
|
}
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3552
|
+
catch (error) {
|
|
3553
|
+
console.error(`🚀 🐥 ~ useScrollmap ~ renderScrollmap:`, error);
|
|
3554
|
+
}
|
|
3555
|
+
}, [vizRef, scrollmap, isDomLoaded]);
|
|
3556
|
+
const renderScrollBucket = useCallback(() => {
|
|
3557
|
+
if (!vizRef || !scrollmap || scrollmap.length === 0 || !isDomLoaded)
|
|
3381
3558
|
return;
|
|
3559
|
+
const bucketData = scrollmap.map((point) => ({
|
|
3560
|
+
position: point.scrollReachY,
|
|
3561
|
+
value: point.cumulativeSum,
|
|
3562
|
+
percent: point.percUsers,
|
|
3563
|
+
}));
|
|
3382
3564
|
try {
|
|
3383
|
-
vizRef?.
|
|
3384
|
-
vizRef?.scrollmap?.(scrollmap);
|
|
3565
|
+
requestIdleCallback(() => vizRef?.scrollBucket?.(bucketData), { timeout: 300 });
|
|
3385
3566
|
}
|
|
3386
3567
|
catch (error) {
|
|
3387
|
-
|
|
3568
|
+
console.error(`🚀 🐥 ~ useScrollmap ~ renderScrollBucket:`, error);
|
|
3388
3569
|
}
|
|
3389
|
-
}, [vizRef, scrollmap]);
|
|
3570
|
+
}, [vizRef, scrollmap, isDomLoaded]);
|
|
3571
|
+
const start = useCallback(() => {
|
|
3572
|
+
vizRef?.clearmap?.();
|
|
3573
|
+
if (!vizRef || !scrollmap || scrollmap.length === 0 || !isDomLoaded)
|
|
3574
|
+
return;
|
|
3575
|
+
switch (scrollType) {
|
|
3576
|
+
case EScrollType.Attention:
|
|
3577
|
+
case EScrollType.Revenue: {
|
|
3578
|
+
renderScrollBucket();
|
|
3579
|
+
break;
|
|
3580
|
+
}
|
|
3581
|
+
case EScrollType.Depth:
|
|
3582
|
+
default:
|
|
3583
|
+
renderScrollmap();
|
|
3584
|
+
break;
|
|
3585
|
+
}
|
|
3586
|
+
}, [vizRef, scrollmap, isDomLoaded, scrollType, renderScrollmap, renderScrollBucket]);
|
|
3390
3587
|
return { start };
|
|
3391
3588
|
};
|
|
3392
3589
|
|
|
@@ -3550,7 +3747,7 @@ const useHeatmapEffects = ({ isVisible }) => {
|
|
|
3550
3747
|
};
|
|
3551
3748
|
|
|
3552
3749
|
const useHeatmapElementPosition = ({ iframeRef, wrapperRef, visualizer }) => {
|
|
3553
|
-
const
|
|
3750
|
+
const viewport = useHeatmapViewportByDevice();
|
|
3554
3751
|
const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
3555
3752
|
const widthScale = useHeatmapVizContext((s) => s.widthScale);
|
|
3556
3753
|
const getRect = useCallback((element) => {
|
|
@@ -3578,17 +3775,17 @@ const useHeatmapElementPosition = ({ iframeRef, wrapperRef, visualizer }) => {
|
|
|
3578
3775
|
const outOfBounds = adjustedTop < 0 ||
|
|
3579
3776
|
adjustedTop > (iframeHeight || Infinity) ||
|
|
3580
3777
|
layout.left < 0 ||
|
|
3581
|
-
(typeof
|
|
3778
|
+
(typeof viewport.width === 'number' && layout.left > viewport.width);
|
|
3582
3779
|
if (outOfBounds)
|
|
3583
3780
|
return null;
|
|
3584
3781
|
return {
|
|
3585
3782
|
left: layout.left,
|
|
3586
3783
|
top: adjustedTop,
|
|
3587
|
-
width: Math.min(layout.width,
|
|
3784
|
+
width: Math.min(layout.width, viewport.width || layout.width),
|
|
3588
3785
|
height: layout.height,
|
|
3589
3786
|
outOfBounds,
|
|
3590
3787
|
};
|
|
3591
|
-
}, [iframeRef, wrapperRef, visualizer,
|
|
3788
|
+
}, [iframeRef, wrapperRef, visualizer, viewport, iframeHeight, widthScale]);
|
|
3592
3789
|
return { getRect };
|
|
3593
3790
|
};
|
|
3594
3791
|
|
|
@@ -3855,7 +4052,7 @@ var MessageType;
|
|
|
3855
4052
|
})(MessageType || (MessageType = {}));
|
|
3856
4053
|
function useVizLiveIframeMsg(options = {}) {
|
|
3857
4054
|
const { trustedOrigins = [], onMessage } = options;
|
|
3858
|
-
const addPayload =
|
|
4055
|
+
const addPayload = useHeatmapLiveContext((s) => s.addPayload);
|
|
3859
4056
|
const [isReady, setIsReady] = useState(false);
|
|
3860
4057
|
const iframeRef = useRef(null);
|
|
3861
4058
|
const isValidOrigin = useCallback((origin) => {
|
|
@@ -3881,7 +4078,7 @@ function useVizLiveIframeMsg(options = {}) {
|
|
|
3881
4078
|
switch (message.type) {
|
|
3882
4079
|
case MessageType.GX_DOM_TRACKING_PAYLOAD:
|
|
3883
4080
|
if (message.payload) {
|
|
3884
|
-
const data =
|
|
4081
|
+
const data = JSON.parse(message.payload);
|
|
3885
4082
|
if (data) {
|
|
3886
4083
|
addPayload(data);
|
|
3887
4084
|
}
|
|
@@ -3904,21 +4101,385 @@ function useVizLiveIframeMsg(options = {}) {
|
|
|
3904
4101
|
};
|
|
3905
4102
|
}
|
|
3906
4103
|
|
|
4104
|
+
/**
|
|
4105
|
+
* Unified performance timing utility.
|
|
4106
|
+
*
|
|
4107
|
+
* Two complementary tools:
|
|
4108
|
+
*
|
|
4109
|
+
* 1. `perf` — global DevTools session recorder.
|
|
4110
|
+
* Stores structured timing in `window.__gemxPerf` for inspection.
|
|
4111
|
+
* Used by the iframe-processor rendering pipeline.
|
|
4112
|
+
*
|
|
4113
|
+
* perf.startSession('render-1');
|
|
4114
|
+
* const t = perf.mark('viewport.run');
|
|
4115
|
+
* perf.measure('viewport.run', t);
|
|
4116
|
+
* perf.endSession();
|
|
4117
|
+
*
|
|
4118
|
+
* 2. `createPerfTimer` — per-module console logger factory.
|
|
4119
|
+
* Logs prefixed timings to the console AND records entries into the
|
|
4120
|
+
* active global session so they appear in `window.__gemxPerf` too.
|
|
4121
|
+
*
|
|
4122
|
+
* const timer = createPerfTimer('Render');
|
|
4123
|
+
* const t0 = timer.mark('start');
|
|
4124
|
+
* await timer.wrap('visualizer.html', () => visualizer.html(...));
|
|
4125
|
+
* timer.measure('total', t0);
|
|
4126
|
+
*/
|
|
4127
|
+
const s = {
|
|
4128
|
+
enabled: true,
|
|
4129
|
+
current: null,
|
|
4130
|
+
sessions: [],
|
|
4131
|
+
maxSessions: 20,
|
|
4132
|
+
};
|
|
4133
|
+
// ── Global singleton functions ────────────────────────────────────────────────
|
|
4134
|
+
function startSession(id) {
|
|
4135
|
+
if (!s.enabled)
|
|
4136
|
+
return;
|
|
4137
|
+
s.current = { id, startedAt: performance.now(), entries: [] };
|
|
4138
|
+
}
|
|
4139
|
+
function endSession() {
|
|
4140
|
+
if (!s.enabled || !s.current)
|
|
4141
|
+
return null;
|
|
4142
|
+
const session = s.current;
|
|
4143
|
+
session.total = performance.now() - session.startedAt;
|
|
4144
|
+
s.sessions = [session, ...s.sessions].slice(0, s.maxSessions);
|
|
4145
|
+
s.current = null;
|
|
4146
|
+
flush();
|
|
4147
|
+
return session;
|
|
4148
|
+
}
|
|
4149
|
+
/** Record a point-in-time mark. Returns `performance.now()` for use with measure(). */
|
|
4150
|
+
function globalMark(label) {
|
|
4151
|
+
const now = performance.now();
|
|
4152
|
+
if (s.enabled && s.current) {
|
|
4153
|
+
s.current.entries.push({ label, t: now - s.current.startedAt });
|
|
4154
|
+
}
|
|
4155
|
+
return now;
|
|
4156
|
+
}
|
|
4157
|
+
/** Record a duration from a previous mark() timestamp. */
|
|
4158
|
+
function globalMeasure(label, t0) {
|
|
4159
|
+
const duration = performance.now() - t0;
|
|
4160
|
+
if (s.enabled && s.current) {
|
|
4161
|
+
s.current.entries.push({ label, t: t0 - s.current.startedAt, duration });
|
|
4162
|
+
}
|
|
4163
|
+
return duration;
|
|
4164
|
+
}
|
|
4165
|
+
function getReport() {
|
|
4166
|
+
return {
|
|
4167
|
+
sessions: s.sessions,
|
|
4168
|
+
latest: s.sessions[0] ?? null,
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
4171
|
+
function clearSessions() {
|
|
4172
|
+
s.current = null;
|
|
4173
|
+
s.sessions = [];
|
|
4174
|
+
if (typeof window !== 'undefined')
|
|
4175
|
+
delete window.__gemxPerf;
|
|
4176
|
+
}
|
|
4177
|
+
function enableGlobal() {
|
|
4178
|
+
s.enabled = true;
|
|
4179
|
+
}
|
|
4180
|
+
function disableGlobal() {
|
|
4181
|
+
s.enabled = false;
|
|
4182
|
+
}
|
|
4183
|
+
function flush() {
|
|
4184
|
+
if (typeof window === 'undefined')
|
|
4185
|
+
return;
|
|
4186
|
+
window.__gemxPerf = getReport();
|
|
4187
|
+
}
|
|
4188
|
+
// ── Global singleton export ───────────────────────────────────────────────────
|
|
4189
|
+
const perf$3 = {
|
|
4190
|
+
startSession,
|
|
4191
|
+
endSession,
|
|
4192
|
+
mark: globalMark,
|
|
4193
|
+
measure: globalMeasure,
|
|
4194
|
+
getReport,
|
|
4195
|
+
clear: clearSessions,
|
|
4196
|
+
enable: enableGlobal,
|
|
4197
|
+
disable: disableGlobal,
|
|
4198
|
+
};
|
|
4199
|
+
// ── createPerfTimer factory ───────────────────────────────────────────────────
|
|
4200
|
+
function createPerfTimer(config) {
|
|
4201
|
+
let cfg = typeof config === 'string' ? { prefix: config, enabled: true } : { enabled: true, ...config };
|
|
4202
|
+
function log(icon, label, extra) {
|
|
4203
|
+
if (!cfg.enabled)
|
|
4204
|
+
return;
|
|
4205
|
+
const suffix = extra ? ` — ${extra}` : '';
|
|
4206
|
+
console.log(`[${cfg.prefix}] ${icon} ${label}${suffix}`);
|
|
4207
|
+
}
|
|
4208
|
+
return {
|
|
4209
|
+
configure(next) {
|
|
4210
|
+
cfg = { ...cfg, ...next };
|
|
4211
|
+
},
|
|
4212
|
+
mark(label) {
|
|
4213
|
+
const t = globalMark(`[${cfg.prefix}] ${label}`);
|
|
4214
|
+
log('⏱', label);
|
|
4215
|
+
return t;
|
|
4216
|
+
},
|
|
4217
|
+
measure(label, from) {
|
|
4218
|
+
globalMeasure(`[${cfg.prefix}] ${label}`, from);
|
|
4219
|
+
const ms = (performance.now() - from).toFixed(1);
|
|
4220
|
+
log('✅', label, `${ms}ms`);
|
|
4221
|
+
},
|
|
4222
|
+
async wrap(label, fn) {
|
|
4223
|
+
const t = globalMark(`[${cfg.prefix}] ${label}`);
|
|
4224
|
+
log('⏱', label);
|
|
4225
|
+
const result = await fn();
|
|
4226
|
+
const duration = globalMeasure(`[${cfg.prefix}] ${label}`, t);
|
|
4227
|
+
log('✅', label, `${duration.toFixed(1)}ms`);
|
|
4228
|
+
return result;
|
|
4229
|
+
},
|
|
4230
|
+
};
|
|
4231
|
+
}
|
|
4232
|
+
|
|
3907
4233
|
/**
|
|
3908
4234
|
* DOM observation setup — ResizeObserver + MutationObserver.
|
|
3909
4235
|
* Returns a cleanup function that disconnects both observers.
|
|
3910
4236
|
*/
|
|
3911
|
-
createLogger({ enabled: false, prefix: 'IframeHeightObserver' });
|
|
4237
|
+
const logger$2 = createLogger({ enabled: false, prefix: 'IframeHeightObserver' });
|
|
4238
|
+
function setup(doc, onChange) {
|
|
4239
|
+
const resizeObserver = new ResizeObserver(onChange);
|
|
4240
|
+
resizeObserver.observe(doc.documentElement);
|
|
4241
|
+
resizeObserver.observe(doc.body);
|
|
4242
|
+
const mutationObserver = new MutationObserver(onChange);
|
|
4243
|
+
mutationObserver.observe(doc.body, {
|
|
4244
|
+
childList: true,
|
|
4245
|
+
subtree: true,
|
|
4246
|
+
attributes: true,
|
|
4247
|
+
attributeFilter: ['style', 'class', 'hidden', 'data-v'],
|
|
4248
|
+
});
|
|
4249
|
+
logger$2.log('DOM observers started (ResizeObserver + MutationObserver)');
|
|
4250
|
+
return () => {
|
|
4251
|
+
resizeObserver.disconnect();
|
|
4252
|
+
mutationObserver.disconnect();
|
|
4253
|
+
logger$2.log('DOM observers disconnected');
|
|
4254
|
+
};
|
|
4255
|
+
}
|
|
3912
4256
|
|
|
3913
4257
|
/**
|
|
3914
4258
|
* Height Observer Processor
|
|
3915
4259
|
* Background observer — watches for iframe content height changes.
|
|
3916
4260
|
*/
|
|
3917
|
-
|
|
4261
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
4262
|
+
function clearTimers(s) {
|
|
4263
|
+
if (s.throttleTimeout) {
|
|
4264
|
+
clearTimeout(s.throttleTimeout);
|
|
4265
|
+
s.throttleTimeout = null;
|
|
4266
|
+
}
|
|
4267
|
+
if (s.debounceTimeout) {
|
|
4268
|
+
clearTimeout(s.debounceTimeout);
|
|
4269
|
+
s.debounceTimeout = null;
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
function getActualHeight(s) {
|
|
4273
|
+
if (!s.iframe?.contentDocument)
|
|
4274
|
+
return 0;
|
|
4275
|
+
const { documentElement: docEl, body } = s.iframe.contentDocument;
|
|
4276
|
+
const heights = [docEl.scrollHeight, docEl.offsetHeight, body.scrollHeight, body.offsetHeight];
|
|
4277
|
+
const maxHeight = Math.max(...heights.filter((h) => h > 0));
|
|
4278
|
+
s.logger.log('Height sources:', {
|
|
4279
|
+
'documentElement.scrollHeight': docEl.scrollHeight,
|
|
4280
|
+
'documentElement.offsetHeight': docEl.offsetHeight,
|
|
4281
|
+
'body.scrollHeight': body.scrollHeight,
|
|
4282
|
+
'body.offsetHeight': body.offsetHeight,
|
|
4283
|
+
maxHeight,
|
|
4284
|
+
});
|
|
4285
|
+
return maxHeight;
|
|
4286
|
+
}
|
|
4287
|
+
async function processHeightChange(s, newHeight) {
|
|
4288
|
+
if (!s.iframe || !s.config)
|
|
4289
|
+
return;
|
|
4290
|
+
s.isProcessing = true;
|
|
4291
|
+
s.logger.log(`Processing height change: ${newHeight}px`);
|
|
4292
|
+
try {
|
|
4293
|
+
const result = {
|
|
4294
|
+
height: newHeight,
|
|
4295
|
+
width: s.iframe.contentWindow?.innerWidth ?? 0,
|
|
4296
|
+
};
|
|
4297
|
+
s.lastHeight = newHeight;
|
|
4298
|
+
s.logger.log('Height change processed:', result);
|
|
4299
|
+
s.config.onHeightChange?.(result);
|
|
4300
|
+
window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', { detail: result }));
|
|
4301
|
+
}
|
|
4302
|
+
catch (error) {
|
|
4303
|
+
s.logger.error('Failed to process height change:', error);
|
|
4304
|
+
s.config.onError?.(error);
|
|
4305
|
+
}
|
|
4306
|
+
finally {
|
|
4307
|
+
s.isProcessing = false;
|
|
4308
|
+
}
|
|
4309
|
+
}
|
|
4310
|
+
function handleHeightChange(s) {
|
|
4311
|
+
if (s.isProcessing || s.throttleTimeout)
|
|
4312
|
+
return;
|
|
4313
|
+
s.throttleTimeout = setTimeout(() => {
|
|
4314
|
+
s.throttleTimeout = null;
|
|
4315
|
+
const currentHeight = getActualHeight(s);
|
|
4316
|
+
if (currentHeight === s.lastHeight) {
|
|
4317
|
+
// Height returned to known state — cancel any stale pending debounce
|
|
4318
|
+
// to prevent it from firing with an outdated height and corrupting lastHeight.
|
|
4319
|
+
if (s.debounceTimeout) {
|
|
4320
|
+
clearTimeout(s.debounceTimeout);
|
|
4321
|
+
s.debounceTimeout = null;
|
|
4322
|
+
}
|
|
4323
|
+
return;
|
|
4324
|
+
}
|
|
4325
|
+
s.logger.log(`Height changed: ${s.lastHeight}px -> ${currentHeight}px`);
|
|
4326
|
+
if (s.debounceTimeout)
|
|
4327
|
+
clearTimeout(s.debounceTimeout);
|
|
4328
|
+
s.debounceTimeout = setTimeout(() => {
|
|
4329
|
+
s.debounceTimeout = null;
|
|
4330
|
+
// Re-read height at dispatch time to avoid using a stale closure value.
|
|
4331
|
+
const finalHeight = getActualHeight(s);
|
|
4332
|
+
if (finalHeight !== s.lastHeight) {
|
|
4333
|
+
processHeightChange(s, finalHeight);
|
|
4334
|
+
}
|
|
4335
|
+
}, s.debounceMs);
|
|
4336
|
+
}, s.throttleMs);
|
|
4337
|
+
}
|
|
4338
|
+
function observe(s) {
|
|
4339
|
+
if (!s.iframe?.contentDocument?.body) {
|
|
4340
|
+
s.logger.warn('Cannot observe height changes: iframe body not found');
|
|
4341
|
+
return;
|
|
4342
|
+
}
|
|
4343
|
+
s.observerCleanup?.();
|
|
4344
|
+
s.lastHeight = getActualHeight(s);
|
|
4345
|
+
s.logger.log('Initial height:', s.lastHeight);
|
|
4346
|
+
s.observerCleanup = setup(s.iframe.contentDocument, () => handleHeightChange(s));
|
|
4347
|
+
}
|
|
4348
|
+
function start$5(s, iframe, cfg) {
|
|
4349
|
+
if (s.running) {
|
|
4350
|
+
s.logger.warn('Observer is already running. Call stop() first.');
|
|
4351
|
+
return;
|
|
4352
|
+
}
|
|
4353
|
+
s.iframe = iframe;
|
|
4354
|
+
s.config = cfg;
|
|
4355
|
+
s.throttleMs = cfg.throttleMs ?? 25;
|
|
4356
|
+
s.debounceMs = cfg.debounceMs ?? 500;
|
|
4357
|
+
s.running = true;
|
|
4358
|
+
observe(s);
|
|
4359
|
+
s.logger.log('Height observer started');
|
|
4360
|
+
}
|
|
4361
|
+
function stop$5(s) {
|
|
4362
|
+
if (!s.running)
|
|
4363
|
+
return;
|
|
4364
|
+
s.observerCleanup?.();
|
|
4365
|
+
s.observerCleanup = null;
|
|
4366
|
+
clearTimers(s);
|
|
4367
|
+
s.iframe = null;
|
|
4368
|
+
s.config = null;
|
|
4369
|
+
s.lastHeight = 0;
|
|
4370
|
+
s.isProcessing = false;
|
|
4371
|
+
s.running = false;
|
|
4372
|
+
s.logger.log('Height observer stopped');
|
|
4373
|
+
}
|
|
4374
|
+
function clear(s) {
|
|
4375
|
+
s.observerCleanup?.();
|
|
4376
|
+
s.observerCleanup = null;
|
|
4377
|
+
clearTimers(s);
|
|
4378
|
+
s.lastHeight = 0;
|
|
4379
|
+
s.isProcessing = false;
|
|
4380
|
+
}
|
|
4381
|
+
function updateConfig$3(s, cfg) {
|
|
4382
|
+
if (!s.running || !s.config) {
|
|
4383
|
+
s.logger.warn('Observer is not running.');
|
|
4384
|
+
return;
|
|
4385
|
+
}
|
|
4386
|
+
s.config = { ...s.config, ...cfg };
|
|
4387
|
+
if (cfg.throttleMs !== undefined)
|
|
4388
|
+
s.throttleMs = cfg.throttleMs;
|
|
4389
|
+
if (cfg.debounceMs !== undefined)
|
|
4390
|
+
s.debounceMs = cfg.debounceMs;
|
|
4391
|
+
s.logger.configure({ enabled: !!s.config.debug });
|
|
4392
|
+
s.logger.log('Config updated');
|
|
4393
|
+
}
|
|
4394
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
4395
|
+
function createHeightObserver() {
|
|
4396
|
+
const s = {
|
|
4397
|
+
logger: createLogger({ enabled: true, prefix: 'IframeHeightObserver' }),
|
|
4398
|
+
iframe: null,
|
|
4399
|
+
config: null,
|
|
4400
|
+
observerCleanup: null,
|
|
4401
|
+
lastHeight: 0,
|
|
4402
|
+
throttleTimeout: null,
|
|
4403
|
+
debounceTimeout: null,
|
|
4404
|
+
isProcessing: false,
|
|
4405
|
+
throttleMs: 25,
|
|
4406
|
+
debounceMs: 500,
|
|
4407
|
+
running: false,
|
|
4408
|
+
};
|
|
4409
|
+
return {
|
|
4410
|
+
start: (iframe, cfg) => start$5(s, iframe, cfg),
|
|
4411
|
+
stop: () => stop$5(s),
|
|
4412
|
+
observe: () => observe(s),
|
|
4413
|
+
clear: () => clear(s),
|
|
4414
|
+
updateConfig: (cfg) => updateConfig$3(s, cfg),
|
|
4415
|
+
getCurrentHeight: () => s.lastHeight,
|
|
4416
|
+
isRunning: () => s.running,
|
|
4417
|
+
getStateInfo: () => ({
|
|
4418
|
+
isRunning: s.running,
|
|
4419
|
+
lastHeight: s.lastHeight,
|
|
4420
|
+
isProcessing: s.isProcessing,
|
|
4421
|
+
hasObservers: !!s.observerCleanup,
|
|
4422
|
+
}),
|
|
4423
|
+
};
|
|
4424
|
+
}
|
|
4425
|
+
|
|
4426
|
+
/**
|
|
4427
|
+
* Window-level event management for the navigation processor.
|
|
4428
|
+
*
|
|
4429
|
+
* Responsibilities:
|
|
4430
|
+
* - Subscribe to events dispatched by this processor (for logging/hooks)
|
|
4431
|
+
* - Dispatch navigation events to the parent window
|
|
4432
|
+
*/
|
|
4433
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
4434
|
+
function attach$1(s, debug) {
|
|
4435
|
+
s.logger.configure({ enabled: !!debug });
|
|
4436
|
+
s.navigationBlockedListener = (e) => {
|
|
4437
|
+
const ev = e;
|
|
4438
|
+
s.logger.log('Navigation blocked:', ev.detail.url);
|
|
4439
|
+
};
|
|
4440
|
+
s.formSubmitWindowListener = (e) => {
|
|
4441
|
+
const ev = e;
|
|
4442
|
+
s.logger.log('Form submitted:', ev.detail.data);
|
|
4443
|
+
};
|
|
4444
|
+
window.addEventListener('iframe-navigation-blocked', s.navigationBlockedListener);
|
|
4445
|
+
window.addEventListener('iframe-form-submit', s.formSubmitWindowListener);
|
|
4446
|
+
}
|
|
4447
|
+
function detach$1(s) {
|
|
4448
|
+
if (s.navigationBlockedListener) {
|
|
4449
|
+
window.removeEventListener('iframe-navigation-blocked', s.navigationBlockedListener);
|
|
4450
|
+
s.navigationBlockedListener = null;
|
|
4451
|
+
}
|
|
4452
|
+
if (s.formSubmitWindowListener) {
|
|
4453
|
+
window.removeEventListener('iframe-form-submit', s.formSubmitWindowListener);
|
|
4454
|
+
s.formSubmitWindowListener = null;
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
function dispatchBlocked(url, showMessage) {
|
|
4458
|
+
if (showMessage)
|
|
4459
|
+
alert(`Navigation blocked: ${url}`);
|
|
4460
|
+
window.dispatchEvent(new CustomEvent('iframe-navigation-blocked', { detail: { url } }));
|
|
4461
|
+
}
|
|
4462
|
+
function dispatchFormSubmit(form, data) {
|
|
4463
|
+
window.dispatchEvent(new CustomEvent('iframe-form-submit', { detail: { form, data } }));
|
|
4464
|
+
}
|
|
4465
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
4466
|
+
function createNavigationListeners() {
|
|
4467
|
+
const s = {
|
|
4468
|
+
logger: createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' }),
|
|
4469
|
+
navigationBlockedListener: null,
|
|
4470
|
+
formSubmitWindowListener: null,
|
|
4471
|
+
};
|
|
4472
|
+
return {
|
|
4473
|
+
attach: (debug) => attach$1(s, debug),
|
|
4474
|
+
detach: () => detach$1(s),
|
|
4475
|
+
dispatchBlocked,
|
|
4476
|
+
dispatchFormSubmit,
|
|
4477
|
+
};
|
|
4478
|
+
}
|
|
3918
4479
|
|
|
3919
|
-
const logger$
|
|
4480
|
+
const logger$1 = createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' });
|
|
3920
4481
|
function configure$1(debug) {
|
|
3921
|
-
logger$
|
|
4482
|
+
logger$1.configure({ enabled: debug });
|
|
3922
4483
|
}
|
|
3923
4484
|
// ─── DOM Utilities ────────────────────────────────────────────────────────────
|
|
3924
4485
|
function disableAllLinks(doc) {
|
|
@@ -3942,10 +4503,10 @@ function setupLinkBlocker(doc, isEnabled, onBlocked) {
|
|
|
3942
4503
|
return;
|
|
3943
4504
|
const href = link.getAttribute('href');
|
|
3944
4505
|
if (!href || href === '' || href === '#' || href.startsWith('#')) {
|
|
3945
|
-
logger$
|
|
4506
|
+
logger$1.log('Allowed hash navigation:', href);
|
|
3946
4507
|
return;
|
|
3947
4508
|
}
|
|
3948
|
-
logger$
|
|
4509
|
+
logger$1.log('Blocked link navigation to:', href);
|
|
3949
4510
|
e.preventDefault();
|
|
3950
4511
|
e.stopPropagation();
|
|
3951
4512
|
e.stopImmediatePropagation();
|
|
@@ -3959,7 +4520,7 @@ function setupLinkBlocker(doc, isEnabled, onBlocked) {
|
|
|
3959
4520
|
return;
|
|
3960
4521
|
const href = link.getAttribute('href');
|
|
3961
4522
|
if (href && !href.startsWith('#')) {
|
|
3962
|
-
logger$
|
|
4523
|
+
logger$1.log('Blocked auxclick navigation');
|
|
3963
4524
|
e.preventDefault();
|
|
3964
4525
|
e.stopPropagation();
|
|
3965
4526
|
e.stopImmediatePropagation();
|
|
@@ -3980,7 +4541,7 @@ function setupFormBlocker(doc, isEnabled, onBlocked, onFormSubmit) {
|
|
|
3980
4541
|
const form = e.target;
|
|
3981
4542
|
const action = form.getAttribute('action');
|
|
3982
4543
|
if (!action || action === '' || action === '#') {
|
|
3983
|
-
logger$
|
|
4544
|
+
logger$1.log('Allowed same-page form');
|
|
3984
4545
|
e.preventDefault();
|
|
3985
4546
|
const data = {};
|
|
3986
4547
|
new FormData(form).forEach((value, key) => {
|
|
@@ -3989,7 +4550,7 @@ function setupFormBlocker(doc, isEnabled, onBlocked, onFormSubmit) {
|
|
|
3989
4550
|
onFormSubmit(form, data);
|
|
3990
4551
|
return;
|
|
3991
4552
|
}
|
|
3992
|
-
logger$
|
|
4553
|
+
logger$1.log('Blocked form submission to:', action);
|
|
3993
4554
|
e.preventDefault();
|
|
3994
4555
|
e.stopPropagation();
|
|
3995
4556
|
e.stopImmediatePropagation();
|
|
@@ -4003,7 +4564,7 @@ function setupWindowOpenBlocker(win, originalOpen, isEnabled, onBlocked) {
|
|
|
4003
4564
|
if (!isEnabled())
|
|
4004
4565
|
return originalOpen(...args);
|
|
4005
4566
|
const url = args[0]?.toString() || 'popup';
|
|
4006
|
-
logger$
|
|
4567
|
+
logger$1.log('Blocked window.open:', url);
|
|
4007
4568
|
onBlocked(url);
|
|
4008
4569
|
return null;
|
|
4009
4570
|
});
|
|
@@ -4015,14 +4576,14 @@ function setupUnloadBlocker(win, isEnabled) {
|
|
|
4015
4576
|
const beforeUnloadListener = (e) => {
|
|
4016
4577
|
if (!isEnabled())
|
|
4017
4578
|
return;
|
|
4018
|
-
logger$
|
|
4579
|
+
logger$1.log('Blocked beforeunload');
|
|
4019
4580
|
e.preventDefault();
|
|
4020
4581
|
e.returnValue = '';
|
|
4021
4582
|
};
|
|
4022
4583
|
const unloadListener = (e) => {
|
|
4023
4584
|
if (!isEnabled())
|
|
4024
4585
|
return;
|
|
4025
|
-
logger$
|
|
4586
|
+
logger$1.log('Blocked unload');
|
|
4026
4587
|
e.preventDefault();
|
|
4027
4588
|
e.stopPropagation();
|
|
4028
4589
|
};
|
|
@@ -4039,65 +4600,14 @@ function setupDOMMonitor(doc) {
|
|
|
4039
4600
|
return () => observer.disconnect();
|
|
4040
4601
|
}
|
|
4041
4602
|
|
|
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
4603
|
/**
|
|
4088
4604
|
* Navigation Processor
|
|
4089
4605
|
* Continuous guard — blocks all navigation attempts within the iframe.
|
|
4090
4606
|
*/
|
|
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.');
|
|
4607
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
4608
|
+
function start$4(s, iframe, cfg) {
|
|
4609
|
+
if (s.running) {
|
|
4610
|
+
s.logger.warn('Blocker is already running. Call stop() first.');
|
|
4101
4611
|
return;
|
|
4102
4612
|
}
|
|
4103
4613
|
if (!iframe.contentDocument || !iframe.contentWindow) {
|
|
@@ -4106,76 +4616,114 @@ function start$5(iframe, cfg) {
|
|
|
4106
4616
|
const doc = iframe.contentDocument;
|
|
4107
4617
|
const win = iframe.contentWindow;
|
|
4108
4618
|
const originalOpen = win.open.bind(win);
|
|
4109
|
-
logger
|
|
4619
|
+
s.logger.configure({ enabled: !!cfg?.debug });
|
|
4110
4620
|
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),
|
|
4621
|
+
s.cleanups = [
|
|
4622
|
+
setupLinkBlocker(doc, () => s.isEnabled, (url) => s.listeners.dispatchBlocked(url, s.showMessage)),
|
|
4623
|
+
setupFormBlocker(doc, () => s.isEnabled, (url) => s.listeners.dispatchBlocked(url, s.showMessage), s.listeners.dispatchFormSubmit),
|
|
4624
|
+
setupWindowOpenBlocker(win, originalOpen, () => s.isEnabled, (url) => s.listeners.dispatchBlocked(url, s.showMessage)),
|
|
4625
|
+
setupUnloadBlocker(win, () => s.isEnabled),
|
|
4116
4626
|
setupDOMMonitor(doc),
|
|
4117
4627
|
];
|
|
4118
|
-
attach
|
|
4119
|
-
running
|
|
4120
|
-
logger
|
|
4628
|
+
s.listeners.attach(cfg?.debug);
|
|
4629
|
+
s.running = true;
|
|
4630
|
+
s.logger.log('Started');
|
|
4631
|
+
}
|
|
4632
|
+
function stop$4(s) {
|
|
4633
|
+
if (!s.running)
|
|
4634
|
+
return;
|
|
4635
|
+
s.cleanups.forEach((fn) => fn());
|
|
4636
|
+
s.cleanups = [];
|
|
4637
|
+
s.listeners.detach();
|
|
4638
|
+
s.isEnabled = false;
|
|
4639
|
+
s.showMessage = false;
|
|
4640
|
+
s.running = false;
|
|
4641
|
+
s.logger.log('Stopped');
|
|
4642
|
+
}
|
|
4643
|
+
function enable(s) {
|
|
4644
|
+
if (!s.running) {
|
|
4645
|
+
s.logger.warn('Blocker is not running.');
|
|
4646
|
+
return;
|
|
4647
|
+
}
|
|
4648
|
+
s.isEnabled = true;
|
|
4649
|
+
s.logger.log('Navigation blocking enabled');
|
|
4650
|
+
}
|
|
4651
|
+
function disable(s) {
|
|
4652
|
+
if (!s.running) {
|
|
4653
|
+
s.logger.warn('Blocker is not running.');
|
|
4654
|
+
return;
|
|
4655
|
+
}
|
|
4656
|
+
s.isEnabled = false;
|
|
4657
|
+
s.logger.log('Navigation blocking disabled');
|
|
4121
4658
|
}
|
|
4122
|
-
function
|
|
4123
|
-
if (!running
|
|
4659
|
+
function enableMessage(s) {
|
|
4660
|
+
if (!s.running) {
|
|
4661
|
+
s.logger.warn('Blocker is not running.');
|
|
4124
4662
|
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.');
|
|
4663
|
+
}
|
|
4664
|
+
s.showMessage = true;
|
|
4665
|
+
s.logger.log('Navigation blocking message enabled');
|
|
4666
|
+
}
|
|
4667
|
+
function disableMessage(s) {
|
|
4668
|
+
if (!s.running) {
|
|
4669
|
+
s.logger.warn('Blocker is not running.');
|
|
4136
4670
|
return;
|
|
4137
4671
|
}
|
|
4138
|
-
|
|
4139
|
-
logger
|
|
4672
|
+
s.showMessage = false;
|
|
4673
|
+
s.logger.log('Navigation blocking message disabled');
|
|
4674
|
+
}
|
|
4675
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
4676
|
+
function createNavigationBlocker() {
|
|
4677
|
+
const s = {
|
|
4678
|
+
logger: createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' }),
|
|
4679
|
+
listeners: createNavigationListeners(),
|
|
4680
|
+
isEnabled: false,
|
|
4681
|
+
showMessage: false,
|
|
4682
|
+
running: false,
|
|
4683
|
+
cleanups: [],
|
|
4684
|
+
};
|
|
4685
|
+
return {
|
|
4686
|
+
start: (iframe, cfg) => start$4(s, iframe, cfg),
|
|
4687
|
+
stop: () => stop$4(s),
|
|
4688
|
+
enable: () => enable(s),
|
|
4689
|
+
disable: () => disable(s),
|
|
4690
|
+
enableMessage: () => enableMessage(s),
|
|
4691
|
+
disableMessage: () => disableMessage(s),
|
|
4692
|
+
isRunning: () => s.running,
|
|
4693
|
+
isBlockingEnabled: () => s.isEnabled,
|
|
4694
|
+
getStateInfo: () => ({ isRunning: s.running, isEnabled: s.isEnabled, showMessage: s.showMessage }),
|
|
4695
|
+
};
|
|
4140
4696
|
}
|
|
4141
4697
|
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4698
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
4699
|
+
function attach(s, debug) {
|
|
4700
|
+
s.logger.configure({ enabled: !!debug });
|
|
4701
|
+
s.dimensionsListener = (e) => {
|
|
4702
|
+
// const ev = e as CustomEvent<IframeDimensionsDetail>;
|
|
4703
|
+
// s.logger.log('Dimensions applied:', ev.detail);
|
|
4704
|
+
};
|
|
4705
|
+
window.addEventListener('iframe-dimensions-applied', s.dimensionsListener);
|
|
4149
4706
|
}
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4707
|
+
function detach(s) {
|
|
4708
|
+
if (s.dimensionsListener) {
|
|
4709
|
+
window.removeEventListener('iframe-dimensions-applied', s.dimensionsListener);
|
|
4710
|
+
s.dimensionsListener = null;
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
4714
|
+
function createViewportListeners() {
|
|
4715
|
+
const s = {
|
|
4716
|
+
logger: createLogger({ enabled: false, prefix: 'ViewportReplacer' }),
|
|
4717
|
+
dimensionsListener: null,
|
|
4718
|
+
};
|
|
4719
|
+
return {
|
|
4720
|
+
attach: (debug) => attach(s, debug),
|
|
4721
|
+
detach: () => detach(s),
|
|
4722
|
+
};
|
|
4163
4723
|
}
|
|
4164
4724
|
|
|
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
4725
|
const DEFAULT_TOLERANCE_PX = 5;
|
|
4178
|
-
const VIEWPORT_UNIT_REGEX = /([
|
|
4726
|
+
const VIEWPORT_UNIT_REGEX = /([-.\\d]+)(vh|svh|lvh|dvh|%)/gi;
|
|
4179
4727
|
const DEFAULT_CSS_VALUES = ['none', 'auto', 'normal', '0px'];
|
|
4180
4728
|
const CRITICAL_PROPERTIES = [
|
|
4181
4729
|
'display',
|
|
@@ -4212,423 +4760,215 @@ const CRITICAL_PROPERTIES = [
|
|
|
4212
4760
|
'grid-template-rows',
|
|
4213
4761
|
'gap',
|
|
4214
4762
|
];
|
|
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) {
|
|
4763
|
+
|
|
4764
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
4765
|
+
function getViewportUnitMap(s) {
|
|
4766
|
+
if (!s.config)
|
|
4232
4767
|
throw new Error('Config is not initialized');
|
|
4233
|
-
}
|
|
4234
4768
|
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
|
|
4769
|
+
vh: s.config.targetHeight,
|
|
4770
|
+
svh: s.config.targetHeight,
|
|
4771
|
+
lvh: s.config.targetHeight,
|
|
4772
|
+
dvh: s.config.targetHeight,
|
|
4773
|
+
vw: s.config.targetWidth,
|
|
4774
|
+
svw: s.config.targetWidth,
|
|
4775
|
+
lvw: s.config.targetWidth,
|
|
4776
|
+
dvw: s.config.targetWidth,
|
|
4243
4777
|
};
|
|
4244
4778
|
}
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
*/
|
|
4248
|
-
function calculateExpectedPx(value, unit) {
|
|
4249
|
-
if (!config$2) {
|
|
4779
|
+
function calculateExpectedPx(s, value, unit) {
|
|
4780
|
+
if (!s.config)
|
|
4250
4781
|
throw new Error('Config is not initialized');
|
|
4251
|
-
}
|
|
4252
4782
|
const unitLower = unit.toLowerCase();
|
|
4253
|
-
if (unitLower === '%')
|
|
4254
|
-
return (value / 100) * config
|
|
4255
|
-
|
|
4256
|
-
const unitMap = getViewportUnitMap();
|
|
4257
|
-
return (value / 100) * (unitMap[unitLower] || 0);
|
|
4783
|
+
if (unitLower === '%')
|
|
4784
|
+
return (value / 100) * s.config.targetHeight;
|
|
4785
|
+
return (value / 100) * (getViewportUnitMap(s)[unitLower] || 0);
|
|
4258
4786
|
}
|
|
4259
|
-
/**
|
|
4260
|
-
* Check if a CSS value is a default/initial value that should be skipped
|
|
4261
|
-
*/
|
|
4262
4787
|
function isDefaultCssValue(value) {
|
|
4263
4788
|
return DEFAULT_CSS_VALUES.includes(value);
|
|
4264
4789
|
}
|
|
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) {
|
|
4790
|
+
function shouldReplaceValue(s, computedValue, originalValue, tolerance = DEFAULT_TOLERANCE_PX) {
|
|
4791
|
+
if (!s.config)
|
|
4272
4792
|
return false;
|
|
4273
|
-
}
|
|
4274
|
-
// Parse computed value (should be in px)
|
|
4275
4793
|
const computedPx = parseFloat(computedValue);
|
|
4276
|
-
if (isNaN(computedPx))
|
|
4277
|
-
return false;
|
|
4278
|
-
}
|
|
4279
|
-
// Parse original value to check what it should be
|
|
4794
|
+
if (isNaN(computedPx))
|
|
4795
|
+
return false;
|
|
4280
4796
|
const regex = new RegExp(VIEWPORT_UNIT_REGEX.source, VIEWPORT_UNIT_REGEX.flags);
|
|
4281
4797
|
const match = originalValue.match(regex);
|
|
4282
|
-
if (!match)
|
|
4283
|
-
return false;
|
|
4284
|
-
}
|
|
4798
|
+
if (!match)
|
|
4799
|
+
return false;
|
|
4285
4800
|
const [, value, unit] = match;
|
|
4286
4801
|
const num = parseFloat(value);
|
|
4287
|
-
if (isNaN(num))
|
|
4802
|
+
if (isNaN(num))
|
|
4288
4803
|
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)
|
|
4804
|
+
const expectedPx = calculateExpectedPx(s, num, unit);
|
|
4293
4805
|
const diff = Math.abs(computedPx - expectedPx);
|
|
4294
4806
|
if (diff <= tolerance) {
|
|
4295
|
-
|
|
4296
|
-
logger$5.log(`OK to replace: computed=${computedValue} matches expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
|
|
4807
|
+
s.logger.log(`OK to replace: computed=${computedValue} matches expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
|
|
4297
4808
|
return true;
|
|
4298
4809
|
}
|
|
4299
|
-
|
|
4300
|
-
logger$5.log(`Skip replace: computed=${computedValue} differs from expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px) - keeping current value`);
|
|
4810
|
+
s.logger.log(`Skip replace: computed=${computedValue} differs from expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
|
|
4301
4811
|
return false;
|
|
4302
4812
|
}
|
|
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)) {
|
|
4813
|
+
function applyPropertyWithVerification(s, element, prop, computedValue, originalValue, tolerance = DEFAULT_TOLERANCE_PX) {
|
|
4814
|
+
if (originalValue && shouldReplaceValue(s, computedValue, originalValue, tolerance)) {
|
|
4309
4815
|
element.style.setProperty(prop, computedValue, 'important');
|
|
4310
4816
|
return true;
|
|
4311
4817
|
}
|
|
4312
4818
|
else if (!originalValue) {
|
|
4313
|
-
// No original value tracked, use old behavior
|
|
4314
4819
|
element.style.setProperty(prop, computedValue, 'important');
|
|
4315
4820
|
return true;
|
|
4316
4821
|
}
|
|
4317
4822
|
return false;
|
|
4318
4823
|
}
|
|
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.');
|
|
4824
|
+
// ── Exported module-level functions ───────────────────────────────────────────
|
|
4825
|
+
function start$3(s, d, w, cfg, options = {}) {
|
|
4826
|
+
if (s.running) {
|
|
4827
|
+
s.logger.warn('Enforcer is already running. Call stop() first.');
|
|
4329
4828
|
return;
|
|
4330
4829
|
}
|
|
4331
|
-
doc
|
|
4332
|
-
win
|
|
4333
|
-
config
|
|
4334
|
-
running
|
|
4335
|
-
logger
|
|
4336
|
-
logger
|
|
4830
|
+
s.doc = d;
|
|
4831
|
+
s.win = w;
|
|
4832
|
+
s.config = cfg;
|
|
4833
|
+
s.running = true;
|
|
4834
|
+
s.logger.configure({ enabled: !!options.debug });
|
|
4835
|
+
s.logger.log('Started');
|
|
4337
4836
|
}
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4837
|
+
function stop$3(s) {
|
|
4838
|
+
if (!s.running)
|
|
4839
|
+
return;
|
|
4840
|
+
s.doc = null;
|
|
4841
|
+
s.win = null;
|
|
4842
|
+
s.config = null;
|
|
4843
|
+
s.elementsWithViewportUnits.clear();
|
|
4844
|
+
s.originalValues = new WeakMap();
|
|
4845
|
+
s.running = false;
|
|
4846
|
+
s.logger.log('Stopped');
|
|
4847
|
+
}
|
|
4848
|
+
function reset(s) {
|
|
4849
|
+
if (!s.running) {
|
|
4850
|
+
s.logger.warn('Enforcer is not running. Call start() first.');
|
|
4343
4851
|
return;
|
|
4344
4852
|
}
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
elementsWithViewportUnits$1.clear();
|
|
4349
|
-
originalValues$1 = new WeakMap();
|
|
4350
|
-
running$3 = false;
|
|
4351
|
-
logger$5.log('Computed style enforcer stopped');
|
|
4853
|
+
s.elementsWithViewportUnits.clear();
|
|
4854
|
+
s.originalValues = new WeakMap();
|
|
4855
|
+
s.logger.log('Reset');
|
|
4352
4856
|
}
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
*/
|
|
4357
|
-
function trackElement(element, propertyOriginalValues) {
|
|
4358
|
-
if (!running$3) {
|
|
4359
|
-
logger$5.warn('Enforcer is not running. Call start() first.');
|
|
4857
|
+
function trackElement(s, element, propertyOriginalValues) {
|
|
4858
|
+
if (!s.running) {
|
|
4859
|
+
s.logger.warn('Enforcer is not running. Call start() first.');
|
|
4360
4860
|
return;
|
|
4361
4861
|
}
|
|
4362
|
-
elementsWithViewportUnits
|
|
4363
|
-
|
|
4364
|
-
let elementOriginals = originalValues$1.get(element);
|
|
4862
|
+
s.elementsWithViewportUnits.add(element);
|
|
4863
|
+
let elementOriginals = s.originalValues.get(element);
|
|
4365
4864
|
if (!elementOriginals) {
|
|
4366
4865
|
elementOriginals = new Map();
|
|
4367
|
-
originalValues
|
|
4866
|
+
s.originalValues.set(element, elementOriginals);
|
|
4368
4867
|
}
|
|
4369
|
-
// Merge property original values (don't override existing)
|
|
4370
4868
|
propertyOriginalValues.forEach((value, prop) => {
|
|
4371
|
-
if (!elementOriginals.has(prop))
|
|
4869
|
+
if (!elementOriginals.has(prop))
|
|
4372
4870
|
elementOriginals.set(prop, value);
|
|
4373
|
-
}
|
|
4374
4871
|
});
|
|
4375
4872
|
}
|
|
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.');
|
|
4873
|
+
function processElement(s, element, options = {}) {
|
|
4874
|
+
if (!s.running || !s.doc || !s.win || !s.config) {
|
|
4875
|
+
s.logger.warn('Enforcer is not running.');
|
|
4382
4876
|
return 0;
|
|
4383
4877
|
}
|
|
4384
|
-
if (!elementsWithViewportUnits
|
|
4878
|
+
if (!s.elementsWithViewportUnits.has(element))
|
|
4385
4879
|
return 0;
|
|
4386
|
-
}
|
|
4387
4880
|
const htmlElement = element;
|
|
4388
|
-
const computed = win
|
|
4881
|
+
const computed = s.win.getComputedStyle(htmlElement);
|
|
4389
4882
|
const inlineStyle = htmlElement.style;
|
|
4390
|
-
const elementOriginals = originalValues
|
|
4883
|
+
const elementOriginals = s.originalValues.get(element);
|
|
4391
4884
|
const tolerance = options.tolerance ?? DEFAULT_TOLERANCE_PX;
|
|
4392
4885
|
let count = 0;
|
|
4393
4886
|
CRITICAL_PROPERTIES.forEach((prop) => {
|
|
4394
4887
|
const computedValue = computed.getPropertyValue(prop);
|
|
4395
4888
|
const inlineValue = inlineStyle.getPropertyValue(prop);
|
|
4396
|
-
if (computedValue && (!inlineValue || inlineValue !== computedValue)) {
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
count++;
|
|
4401
|
-
}
|
|
4402
|
-
}
|
|
4889
|
+
if (computedValue && (!inlineValue || inlineValue !== computedValue) && !isDefaultCssValue(computedValue)) {
|
|
4890
|
+
const originalValue = elementOriginals?.get(prop) || '';
|
|
4891
|
+
if (applyPropertyWithVerification(s, htmlElement, prop, computedValue, originalValue, tolerance))
|
|
4892
|
+
count++;
|
|
4403
4893
|
}
|
|
4404
4894
|
});
|
|
4405
4895
|
return count;
|
|
4406
4896
|
}
|
|
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.');
|
|
4897
|
+
function processAll(s, options = {}) {
|
|
4898
|
+
if (!s.running || !s.doc || !s.win || !s.config) {
|
|
4899
|
+
s.logger.warn('Enforcer is not running.');
|
|
4414
4900
|
return 0;
|
|
4415
4901
|
}
|
|
4416
4902
|
let totalCount = 0;
|
|
4417
|
-
elementsWithViewportUnits
|
|
4418
|
-
totalCount += processElement(element, options);
|
|
4903
|
+
s.elementsWithViewportUnits.forEach((element) => {
|
|
4904
|
+
totalCount += processElement(s, element, options);
|
|
4419
4905
|
});
|
|
4420
|
-
logger
|
|
4906
|
+
s.logger.log(`Enforced ${totalCount} computed styles for ${s.elementsWithViewportUnits.size} elements`);
|
|
4421
4907
|
return totalCount;
|
|
4422
4908
|
}
|
|
4909
|
+
function updateConfig$2(s, cfg) {
|
|
4910
|
+
if (!s.running || !s.config) {
|
|
4911
|
+
s.logger.warn('Enforcer is not running.');
|
|
4912
|
+
return;
|
|
4913
|
+
}
|
|
4914
|
+
s.config = { ...s.config, ...cfg };
|
|
4915
|
+
s.logger.log('Config updated:', cfg);
|
|
4916
|
+
}
|
|
4423
4917
|
|
|
4424
4918
|
/**
|
|
4425
|
-
*
|
|
4426
|
-
*
|
|
4919
|
+
* Computed Style Enforcer
|
|
4920
|
+
* Enforces computed CSS styles with viewport unit verification.
|
|
4427
4921
|
*/
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
const
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
}
|
|
4439
|
-
// ─── Unit conversion ─────────────────────────────────────────────────────────
|
|
4440
|
-
function px(value) {
|
|
4441
|
-
return `${value.toFixed(2)}px`;
|
|
4442
|
-
}
|
|
4443
|
-
function getUnitMap(ctx) {
|
|
4922
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
4923
|
+
function createEnforcer() {
|
|
4924
|
+
const s = {
|
|
4925
|
+
logger: createLogger({ enabled: false, prefix: 'ComputedStyleEnforcer' }),
|
|
4926
|
+
doc: null,
|
|
4927
|
+
win: null,
|
|
4928
|
+
config: null,
|
|
4929
|
+
elementsWithViewportUnits: new Set(),
|
|
4930
|
+
originalValues: new WeakMap(),
|
|
4931
|
+
running: false,
|
|
4932
|
+
};
|
|
4444
4933
|
return {
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
return (value / 100) * (getUnitMap(ctx)[u] ?? 0);
|
|
4460
|
-
}
|
|
4461
|
-
function convert(value, unit, ctx) {
|
|
4462
|
-
const num = parseFloat(value);
|
|
4463
|
-
return isNaN(num) ? value : px(toPx(num, unit, ctx));
|
|
4464
|
-
}
|
|
4465
|
-
function isHeightRelated(prop) {
|
|
4466
|
-
return HEIGHT_RELATED_PROPERTIES.includes(prop);
|
|
4934
|
+
start: (d, w, cfg, options) => start$3(s, d, w, cfg, options),
|
|
4935
|
+
stop: () => stop$3(s),
|
|
4936
|
+
reset: () => reset(s),
|
|
4937
|
+
trackElement: (element, propertyOriginalValues) => trackElement(s, element, propertyOriginalValues),
|
|
4938
|
+
processElement: (element, options) => processElement(s, element, options),
|
|
4939
|
+
processAll: (options) => processAll(s, options),
|
|
4940
|
+
updateConfig: (cfg) => updateConfig$2(s, cfg),
|
|
4941
|
+
getStateInfo: () => ({
|
|
4942
|
+
isRunning: s.running,
|
|
4943
|
+
trackedElementsCount: s.elementsWithViewportUnits.size,
|
|
4944
|
+
hasConfig: !!s.config,
|
|
4945
|
+
}),
|
|
4946
|
+
isRunning: () => s.running,
|
|
4947
|
+
};
|
|
4467
4948
|
}
|
|
4949
|
+
|
|
4950
|
+
const registry$1 = [];
|
|
4468
4951
|
/**
|
|
4469
|
-
*
|
|
4470
|
-
*
|
|
4952
|
+
* Register a global fix.
|
|
4953
|
+
* Fixes are run in registration order.
|
|
4471
4954
|
*/
|
|
4472
|
-
function
|
|
4473
|
-
|
|
4474
|
-
const m = before.match(/([a-z-]+)\s*:\s*[^;{}]*$/i);
|
|
4475
|
-
return m ? m[1].toLowerCase() : '';
|
|
4476
|
-
}
|
|
4477
|
-
function replaceInText(cssText, ctx) {
|
|
4478
|
-
return cssText.replace(createRegex(), (match, value, unit, offset) => {
|
|
4479
|
-
if (unit === '%') {
|
|
4480
|
-
return isHeightRelated(extractProperty(cssText, offset)) ? convert(value, unit, ctx) : match;
|
|
4481
|
-
}
|
|
4482
|
-
return convert(value, unit, ctx);
|
|
4483
|
-
});
|
|
4484
|
-
}
|
|
4485
|
-
// ─── Element tracking ─────────────────────────────────────────────────────────
|
|
4486
|
-
function trackSelector(selector, propOriginals, ctx) {
|
|
4487
|
-
try {
|
|
4488
|
-
ctx.doc.querySelectorAll(selector).forEach((el) => {
|
|
4489
|
-
elementsWithViewportUnits.add(el);
|
|
4490
|
-
let originals = originalValues.get(el);
|
|
4491
|
-
if (!originals) {
|
|
4492
|
-
originals = new Map();
|
|
4493
|
-
originalValues.set(el, originals);
|
|
4494
|
-
}
|
|
4495
|
-
propOriginals.forEach((v, k) => {
|
|
4496
|
-
if (!originals.has(k))
|
|
4497
|
-
originals.set(k, v);
|
|
4498
|
-
});
|
|
4499
|
-
trackElement(el, propOriginals);
|
|
4500
|
-
});
|
|
4501
|
-
}
|
|
4502
|
-
catch {
|
|
4503
|
-
logger$4.warn('Invalid selector, skipping:', selector);
|
|
4504
|
-
}
|
|
4505
|
-
}
|
|
4506
|
-
// ─── CSS processing ───────────────────────────────────────────────────────────
|
|
4507
|
-
function processInlineStyles(ctx) {
|
|
4508
|
-
let count = 0;
|
|
4509
|
-
ctx.doc.querySelectorAll('[style]').forEach((el) => {
|
|
4510
|
-
const style = el.getAttribute('style');
|
|
4511
|
-
if (style && createRegex().test(style)) {
|
|
4512
|
-
elementsWithViewportUnits.add(el);
|
|
4513
|
-
el.setAttribute('style', replaceInText(style, ctx));
|
|
4514
|
-
count++;
|
|
4515
|
-
}
|
|
4516
|
-
});
|
|
4517
|
-
logger$4.log(`Replaced ${count} inline style elements`);
|
|
4518
|
-
return count;
|
|
4519
|
-
}
|
|
4520
|
-
function processStyleTags(ctx) {
|
|
4521
|
-
let count = 0;
|
|
4522
|
-
ctx.doc.querySelectorAll('style').forEach((tag) => {
|
|
4523
|
-
const css = tag.textContent || '';
|
|
4524
|
-
if (createRegex().test(css)) {
|
|
4525
|
-
tag.textContent = replaceInText(css, ctx);
|
|
4526
|
-
count++;
|
|
4527
|
-
}
|
|
4528
|
-
});
|
|
4529
|
-
logger$4.log(`Replaced ${count} <style> tags`);
|
|
4530
|
-
return count;
|
|
4531
|
-
}
|
|
4532
|
-
function processRule(rule, ctx) {
|
|
4533
|
-
let count = 0;
|
|
4534
|
-
if ('style' in rule && rule.style) {
|
|
4535
|
-
const cssRule = rule;
|
|
4536
|
-
const style = cssRule.style;
|
|
4537
|
-
let hasVp = false;
|
|
4538
|
-
const propOriginals = new Map();
|
|
4539
|
-
for (let i = 0; i < style.length; i++) {
|
|
4540
|
-
const prop = style[i];
|
|
4541
|
-
const value = style.getPropertyValue(prop);
|
|
4542
|
-
if (value && createRegex().test(value)) {
|
|
4543
|
-
hasVp = true;
|
|
4544
|
-
propOriginals.set(prop, value);
|
|
4545
|
-
style.setProperty(prop, replaceInText(value, ctx), style.getPropertyPriority(prop));
|
|
4546
|
-
count++;
|
|
4547
|
-
}
|
|
4548
|
-
}
|
|
4549
|
-
if (hasVp && cssRule.selectorText)
|
|
4550
|
-
trackSelector(cssRule.selectorText, propOriginals, ctx);
|
|
4551
|
-
}
|
|
4552
|
-
if ('cssRules' in rule) {
|
|
4553
|
-
for (const r of Array.from(rule.cssRules || [])) {
|
|
4554
|
-
count += processRule(r, ctx);
|
|
4555
|
-
}
|
|
4556
|
-
}
|
|
4557
|
-
return count;
|
|
4955
|
+
function register$1(fix) {
|
|
4956
|
+
registry$1.push(fix);
|
|
4558
4957
|
}
|
|
4559
|
-
/**
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4958
|
+
/**
|
|
4959
|
+
* Returns all fixes that are active for the given context.
|
|
4960
|
+
* A fix is active if it has no `shouldApply`, or `shouldApply` returns true.
|
|
4961
|
+
*/
|
|
4962
|
+
function getActiveFixes(ctx) {
|
|
4963
|
+
return registry$1.filter((fix) => {
|
|
4565
4964
|
try {
|
|
4566
|
-
|
|
4567
|
-
total += processRule(rule, ctx);
|
|
4568
|
-
}
|
|
4965
|
+
return !fix.shouldApply || fix.shouldApply(ctx);
|
|
4569
4966
|
}
|
|
4570
|
-
catch
|
|
4571
|
-
|
|
4967
|
+
catch {
|
|
4968
|
+
return false;
|
|
4572
4969
|
}
|
|
4573
4970
|
});
|
|
4574
|
-
logger$4.log(`Replaced ${total} rules in inline stylesheets`);
|
|
4575
|
-
return total;
|
|
4576
4971
|
}
|
|
4577
|
-
async function processLinkedStylesheets(ctx) {
|
|
4578
|
-
const links = ctx.doc.querySelectorAll('link[rel="stylesheet"]');
|
|
4579
|
-
let count = 0;
|
|
4580
|
-
for (const link of Array.from(links)) {
|
|
4581
|
-
// Skip cross-origin — already in browser CSSOM, handled via processStylesheets
|
|
4582
|
-
if (link.href && !link.href.startsWith(ctx.win.location.origin)) {
|
|
4583
|
-
logger$4.log('Skipping cross-origin CSS:', link.href);
|
|
4584
|
-
continue;
|
|
4585
|
-
}
|
|
4586
|
-
try {
|
|
4587
|
-
const res = await fetch(link.href);
|
|
4588
|
-
let css = await res.text();
|
|
4589
|
-
if (createRegex().test(css)) {
|
|
4590
|
-
css = replaceInText(css, ctx);
|
|
4591
|
-
const style = ctx.doc.createElement('style');
|
|
4592
|
-
style.textContent = css;
|
|
4593
|
-
style.dataset.originalHref = link.href;
|
|
4594
|
-
link.parentNode?.insertBefore(style, link);
|
|
4595
|
-
link.remove();
|
|
4596
|
-
count++;
|
|
4597
|
-
}
|
|
4598
|
-
}
|
|
4599
|
-
catch (e) {
|
|
4600
|
-
logger$4.warn('Cannot load CSS:', link.href, e);
|
|
4601
|
-
}
|
|
4602
|
-
}
|
|
4603
|
-
logger$4.log(`Replaced ${count} linked CSS files`);
|
|
4604
|
-
return count;
|
|
4605
|
-
}
|
|
4606
|
-
// ─── Public entry point ───────────────────────────────────────────────────────
|
|
4607
|
-
async function process$1(ctx) {
|
|
4608
|
-
logger$4.configure({ enabled: !!ctx.debug });
|
|
4609
|
-
// Reset tracking state from any previous run
|
|
4610
|
-
elementsWithViewportUnits = new Set();
|
|
4611
|
-
originalValues = new WeakMap();
|
|
4612
|
-
processInlineStyles(ctx);
|
|
4613
|
-
processStyleTags(ctx);
|
|
4614
|
-
processStylesheets(ctx);
|
|
4615
|
-
await processLinkedStylesheets(ctx);
|
|
4616
|
-
// Wait for browser to apply the replaced styles
|
|
4617
|
-
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
4618
|
-
// Enforce final computed styles to inline with !important
|
|
4619
|
-
const count = processAll({ debug: !!ctx.debug });
|
|
4620
|
-
logger$4.log(`Enforced ${count} computed styles for ${elementsWithViewportUnits.size} tracked elements`);
|
|
4621
|
-
}
|
|
4622
|
-
|
|
4623
|
-
/**
|
|
4624
|
-
* Built-in global fix — always runs, no shouldApply condition.
|
|
4625
|
-
* Registered first so it runs before any other global process hooks.
|
|
4626
|
-
*/
|
|
4627
|
-
register$1({
|
|
4628
|
-
name: 'viewport-unit-replacer',
|
|
4629
|
-
description: 'Core: convert vh/vw/svh/dvh/% to px values across all iframe CSS',
|
|
4630
|
-
process: process$1,
|
|
4631
|
-
});
|
|
4632
4972
|
|
|
4633
4973
|
/**
|
|
4634
4974
|
* GemPages v7 Slider Fix
|
|
@@ -4713,6 +5053,9 @@ function getMaxWByDeviceType(deviceType) {
|
|
|
4713
5053
|
// ─── Main fix ─────────────────────────────────────────────────────────────────
|
|
4714
5054
|
function afterProcess$1({ doc, targetWidth, deviceType }) {
|
|
4715
5055
|
doc.querySelectorAll(SLIDER_ITEM_SELECTOR).forEach((item) => {
|
|
5056
|
+
// When !gp-min-w-full is set, the item fills 100% width via Tailwind — skip manual width override
|
|
5057
|
+
if (item.classList.contains('!gp-min-w-full'))
|
|
5058
|
+
return;
|
|
4716
5059
|
const originalWidth = parseFloat(item.style.minWidth) || parseFloat(item.style.maxWidth) || 0;
|
|
4717
5060
|
if (!originalWidth)
|
|
4718
5061
|
return;
|
|
@@ -4813,23 +5156,6 @@ register$1({
|
|
|
4813
5156
|
afterProcess,
|
|
4814
5157
|
});
|
|
4815
5158
|
|
|
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
5159
|
/**
|
|
4834
5160
|
* Default iframe dimension calculation.
|
|
4835
5161
|
* Used as fallback when no fix overrides getDimensions().
|
|
@@ -4853,41 +5179,58 @@ function getFinalWidth(doc) {
|
|
|
4853
5179
|
* Phase 3: afterProcess (shop → global)
|
|
4854
5180
|
* Phase 4: getDimensions (shop → global → default)
|
|
4855
5181
|
*/
|
|
4856
|
-
const logger
|
|
5182
|
+
const logger = createLogger({ enabled: false, prefix: 'ViewportReplacer' });
|
|
4857
5183
|
function configure(debug) {
|
|
4858
|
-
logger
|
|
5184
|
+
logger.configure({ enabled: debug });
|
|
4859
5185
|
}
|
|
4860
5186
|
async function run$1(ctx, activeGlobal, shopFix) {
|
|
4861
5187
|
// ── Phase 1: beforeProcess ────────────────────────────────────────────────
|
|
5188
|
+
const t1 = perf$3.mark('phase1.beforeProcess');
|
|
4862
5189
|
for (const fix of activeGlobal) {
|
|
4863
5190
|
if (fix.beforeProcess) {
|
|
4864
|
-
logger
|
|
5191
|
+
logger.log(`[beforeProcess] ${fix.name}`);
|
|
5192
|
+
const t = perf$3.mark(`phase1.${fix.name}.beforeProcess`);
|
|
4865
5193
|
await fix.beforeProcess(ctx);
|
|
5194
|
+
perf$3.measure(`phase1.${fix.name}.beforeProcess`, t);
|
|
4866
5195
|
}
|
|
4867
5196
|
}
|
|
4868
5197
|
if (shopFix?.beforeProcess) {
|
|
4869
|
-
logger
|
|
5198
|
+
logger.log('[beforeProcess] shop');
|
|
5199
|
+
const t = perf$3.mark('phase1.shop.beforeProcess');
|
|
4870
5200
|
await shopFix.beforeProcess(ctx);
|
|
5201
|
+
perf$3.measure('phase1.shop.beforeProcess', t);
|
|
4871
5202
|
}
|
|
5203
|
+
perf$3.measure('phase1.beforeProcess', t1);
|
|
4872
5204
|
// ── Phase 2: process ──────────────────────────────────────────────────────
|
|
5205
|
+
const t2 = perf$3.mark('phase2.process');
|
|
4873
5206
|
for (const fix of activeGlobal) {
|
|
4874
5207
|
if (fix.process) {
|
|
4875
|
-
logger
|
|
5208
|
+
logger.log(`[process] ${fix.name}`);
|
|
5209
|
+
const t = perf$3.mark(`phase2.${fix.name}.process`);
|
|
4876
5210
|
await fix.process(ctx);
|
|
5211
|
+
perf$3.measure(`phase2.${fix.name}.process`, t);
|
|
4877
5212
|
}
|
|
4878
5213
|
}
|
|
5214
|
+
perf$3.measure('phase2.process', t2);
|
|
4879
5215
|
// ── Phase 3: afterProcess ─────────────────────────────────────────────────
|
|
5216
|
+
const t3 = perf$3.mark('phase3.afterProcess');
|
|
4880
5217
|
if (shopFix?.afterProcess) {
|
|
4881
|
-
logger
|
|
5218
|
+
logger.log('[afterProcess] shop');
|
|
5219
|
+
const t = perf$3.mark('phase3.shop.afterProcess');
|
|
4882
5220
|
await shopFix.afterProcess(ctx);
|
|
5221
|
+
perf$3.measure('phase3.shop.afterProcess', t);
|
|
4883
5222
|
}
|
|
4884
5223
|
for (const fix of activeGlobal) {
|
|
4885
5224
|
if (fix.afterProcess) {
|
|
4886
|
-
logger
|
|
5225
|
+
logger.log(`[afterProcess] ${fix.name}`);
|
|
5226
|
+
const t = perf$3.mark(`phase3.${fix.name}.afterProcess`);
|
|
4887
5227
|
await fix.afterProcess(ctx);
|
|
5228
|
+
perf$3.measure(`phase3.${fix.name}.afterProcess`, t);
|
|
4888
5229
|
}
|
|
4889
5230
|
}
|
|
5231
|
+
perf$3.measure('phase3.afterProcess', t3);
|
|
4890
5232
|
// ── Phase 4: getDimensions ────────────────────────────────────────────────
|
|
5233
|
+
const t4 = perf$3.mark('phase4.getDimensions');
|
|
4891
5234
|
return new Promise((resolve) => {
|
|
4892
5235
|
requestAnimationFrame(() => {
|
|
4893
5236
|
let dimensions = null;
|
|
@@ -4895,14 +5238,14 @@ async function run$1(ctx, activeGlobal, shopFix) {
|
|
|
4895
5238
|
if (shopFix?.getDimensions) {
|
|
4896
5239
|
dimensions = shopFix.getDimensions(ctx);
|
|
4897
5240
|
if (dimensions)
|
|
4898
|
-
logger
|
|
5241
|
+
logger.log('Dimensions from shop fix:', dimensions);
|
|
4899
5242
|
}
|
|
4900
5243
|
if (!dimensions) {
|
|
4901
5244
|
for (const fix of activeGlobal) {
|
|
4902
5245
|
if (fix.getDimensions) {
|
|
4903
5246
|
dimensions = fix.getDimensions(ctx);
|
|
4904
5247
|
if (dimensions) {
|
|
4905
|
-
logger
|
|
5248
|
+
logger.log(`Dimensions from global fix [${fix.name}]:`, dimensions);
|
|
4906
5249
|
break;
|
|
4907
5250
|
}
|
|
4908
5251
|
}
|
|
@@ -4911,7 +5254,8 @@ async function run$1(ctx, activeGlobal, shopFix) {
|
|
|
4911
5254
|
if (!dimensions) {
|
|
4912
5255
|
dimensions = { height: getFinalHeight(ctx.doc, ctx.win), width: getFinalWidth(ctx.doc) };
|
|
4913
5256
|
}
|
|
4914
|
-
logger
|
|
5257
|
+
logger.log('Final dimensions:', dimensions);
|
|
5258
|
+
perf$3.measure('phase4.getDimensions', t4);
|
|
4915
5259
|
resolve(dimensions);
|
|
4916
5260
|
});
|
|
4917
5261
|
});
|
|
@@ -4988,233 +5332,302 @@ register(`566240210141053597`, {
|
|
|
4988
5332
|
afterProcess: restoreAndFixLayout,
|
|
4989
5333
|
});
|
|
4990
5334
|
|
|
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.');
|
|
5335
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
5336
|
+
function start$2(s, iframe, cfg) {
|
|
5337
|
+
if (s.running) {
|
|
5338
|
+
s.logger.warn('Already running. Call stop() first.');
|
|
5002
5339
|
return;
|
|
5003
5340
|
}
|
|
5004
5341
|
if (!iframe.contentDocument || !iframe.contentWindow) {
|
|
5005
5342
|
throw new Error('Iframe document or window not accessible');
|
|
5006
5343
|
}
|
|
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 });
|
|
5344
|
+
s.doc = iframe.contentDocument;
|
|
5345
|
+
s.win = iframe.contentWindow;
|
|
5346
|
+
s.config = cfg;
|
|
5347
|
+
s.running = true;
|
|
5348
|
+
s.shopFix = cfg.shopId != null ? get(cfg.shopId) : null;
|
|
5349
|
+
if (s.shopFix)
|
|
5350
|
+
s.logger.log(`Shop fix loaded for "${cfg.shopId}":`, s.shopFix.description ?? '(no description)');
|
|
5351
|
+
s.logger.configure({ enabled: !!cfg.debug });
|
|
5016
5352
|
configure(!!cfg.debug);
|
|
5017
|
-
start
|
|
5018
|
-
attach(cfg.debug);
|
|
5019
|
-
logger
|
|
5353
|
+
s.enforcer.start(s.doc, s.win, cfg, { debug: !!cfg.debug });
|
|
5354
|
+
s.listeners.attach(cfg.debug);
|
|
5355
|
+
s.logger.log('Started');
|
|
5020
5356
|
}
|
|
5021
|
-
function stop$
|
|
5022
|
-
if (!running
|
|
5357
|
+
function stop$2(s) {
|
|
5358
|
+
if (!s.running)
|
|
5023
5359
|
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
|
|
5360
|
+
s.enforcer.stop();
|
|
5361
|
+
s.listeners.detach();
|
|
5362
|
+
s.doc = null;
|
|
5363
|
+
s.win = null;
|
|
5364
|
+
s.config = null;
|
|
5365
|
+
s.shopFix = null;
|
|
5366
|
+
s.running = false;
|
|
5367
|
+
s.logger.log('Stopped');
|
|
5368
|
+
}
|
|
5369
|
+
async function run(s) {
|
|
5370
|
+
if (!s.running || !s.doc || !s.win || !s.config) {
|
|
5371
|
+
s.logger.warn('Not running. Call start() first.');
|
|
5036
5372
|
return { height: 1000, width: 1000 };
|
|
5037
5373
|
}
|
|
5038
5374
|
const ctx = {
|
|
5039
|
-
doc,
|
|
5040
|
-
win,
|
|
5041
|
-
deviceType: config
|
|
5042
|
-
targetWidth: config
|
|
5043
|
-
targetHeight: config
|
|
5044
|
-
debug: config
|
|
5375
|
+
doc: s.doc,
|
|
5376
|
+
win: s.win,
|
|
5377
|
+
deviceType: s.config.deviceType,
|
|
5378
|
+
targetWidth: s.config.targetWidth,
|
|
5379
|
+
targetHeight: s.config.targetHeight,
|
|
5380
|
+
debug: s.config.debug,
|
|
5381
|
+
enforcer: s.enforcer,
|
|
5045
5382
|
};
|
|
5046
5383
|
const activeGlobal = getActiveFixes(ctx);
|
|
5047
|
-
if (activeGlobal.length > 0)
|
|
5048
|
-
logger
|
|
5049
|
-
|
|
5384
|
+
if (activeGlobal.length > 0)
|
|
5385
|
+
s.logger.log(`Active global fixes: ${activeGlobal.map((f) => f.name).join(', ')}`);
|
|
5386
|
+
const tRun = perf$3.mark('viewport.run');
|
|
5050
5387
|
try {
|
|
5051
|
-
|
|
5388
|
+
const result = await run$1(ctx, activeGlobal, s.shopFix);
|
|
5389
|
+
perf$3.measure('viewport.run', tRun);
|
|
5390
|
+
return result;
|
|
5052
5391
|
}
|
|
5053
5392
|
catch (err) {
|
|
5054
|
-
|
|
5055
|
-
|
|
5393
|
+
perf$3.measure('viewport.run', tRun);
|
|
5394
|
+
s.logger.error('Critical error:', err);
|
|
5395
|
+
return { height: s.doc.body?.scrollHeight || 1000, width: s.doc.body?.scrollWidth || 1000 };
|
|
5056
5396
|
}
|
|
5057
5397
|
}
|
|
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.');
|
|
5398
|
+
function updateConfig$1(s, cfg) {
|
|
5399
|
+
if (!s.running || !s.config) {
|
|
5400
|
+
s.logger.warn('Not running. Call start() first.');
|
|
5076
5401
|
return;
|
|
5077
5402
|
}
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
logger.log('
|
|
5083
|
-
|
|
5403
|
+
s.config = { ...s.config, ...cfg };
|
|
5404
|
+
if (cfg.shopId !== undefined)
|
|
5405
|
+
s.shopFix = cfg.shopId != null ? get(cfg.shopId) : null;
|
|
5406
|
+
s.enforcer.updateConfig(cfg);
|
|
5407
|
+
s.logger.log('Config updated');
|
|
5408
|
+
}
|
|
5409
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
5410
|
+
function createViewportProcessor() {
|
|
5411
|
+
const s = {
|
|
5412
|
+
logger: createLogger({ enabled: false, prefix: 'ViewportReplacer' }),
|
|
5413
|
+
enforcer: createEnforcer(),
|
|
5414
|
+
listeners: createViewportListeners(),
|
|
5415
|
+
doc: null,
|
|
5416
|
+
win: null,
|
|
5417
|
+
config: null,
|
|
5418
|
+
shopFix: null,
|
|
5419
|
+
running: false,
|
|
5420
|
+
};
|
|
5421
|
+
return {
|
|
5422
|
+
start: (iframe, cfg) => start$2(s, iframe, cfg),
|
|
5423
|
+
stop: () => stop$2(s),
|
|
5424
|
+
run: () => run(s),
|
|
5425
|
+
updateConfig: (cfg) => updateConfig$1(s, cfg),
|
|
5426
|
+
isRunning: () => s.running,
|
|
5427
|
+
};
|
|
5428
|
+
}
|
|
5429
|
+
|
|
5430
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
5431
|
+
function dispatchDimensionsEvent(dimensions) {
|
|
5432
|
+
window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', { detail: dimensions }));
|
|
5084
5433
|
}
|
|
5085
|
-
function
|
|
5086
|
-
if (!
|
|
5434
|
+
async function process(s) {
|
|
5435
|
+
if (!s.iframe || !s.config)
|
|
5087
5436
|
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'));
|
|
5437
|
+
if (!s.iframe.contentDocument || !s.iframe.contentWindow) {
|
|
5438
|
+
s.logger.error('Cannot access iframe document');
|
|
5439
|
+
s.config.onError?.(new Error('Cannot access iframe document'));
|
|
5106
5440
|
return;
|
|
5107
5441
|
}
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5442
|
+
const sessionId = `render-${Date.now()}`;
|
|
5443
|
+
perf$3.startSession(sessionId);
|
|
5444
|
+
const t0 = perf$3.mark('orchestrator.process');
|
|
5445
|
+
try {
|
|
5446
|
+
s.logger.groupCollapsed('Processing...');
|
|
5447
|
+
s.viewportReplacer.start(s.iframe, s.config);
|
|
5448
|
+
s.navigationBlocker.start(s.iframe, { debug: s.config.debug });
|
|
5449
|
+
const result = await s.viewportReplacer.run();
|
|
5450
|
+
perf$3.measure('orchestrator.process', t0);
|
|
5451
|
+
perf$3.endSession();
|
|
5452
|
+
s.logger.groupEnd();
|
|
5453
|
+
s.logger.log('Process completed:', result);
|
|
5454
|
+
s.heightObserver.stop();
|
|
5455
|
+
s.heightObserver.start(s.iframe, {
|
|
5456
|
+
iframe: s.iframe,
|
|
5457
|
+
debug: s.config.debug,
|
|
5458
|
+
throttleMs: 25,
|
|
5459
|
+
debounceMs: 500,
|
|
5460
|
+
onHeightChange: (result) => {
|
|
5461
|
+
s.config?.onSuccess?.(result);
|
|
5462
|
+
dispatchDimensionsEvent(result);
|
|
5463
|
+
},
|
|
5464
|
+
onError: (error) => {
|
|
5465
|
+
s.config?.onError?.(error);
|
|
5466
|
+
},
|
|
5467
|
+
});
|
|
5468
|
+
s.config.onSuccess?.(result);
|
|
5469
|
+
dispatchDimensionsEvent(result);
|
|
5111
5470
|
}
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5471
|
+
catch (error) {
|
|
5472
|
+
perf$3.measure('orchestrator.process', t0);
|
|
5473
|
+
perf$3.endSession();
|
|
5474
|
+
s.logger.error('Failed to process:', error);
|
|
5475
|
+
s.config.onError?.(error);
|
|
5115
5476
|
}
|
|
5116
5477
|
}
|
|
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'));
|
|
5478
|
+
async function initialize(s) {
|
|
5479
|
+
if (!s.iframe || !s.config) {
|
|
5480
|
+
s.logger.error('iframe not found');
|
|
5481
|
+
s.config?.onError?.(new Error('iframe not found'));
|
|
5123
5482
|
return;
|
|
5124
5483
|
}
|
|
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();
|
|
5484
|
+
if (s.iframe.contentDocument?.readyState === 'complete') {
|
|
5485
|
+
await process(s);
|
|
5135
5486
|
}
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5487
|
+
else {
|
|
5488
|
+
s.loadListener = () => process(s);
|
|
5489
|
+
s.iframe.addEventListener('load', s.loadListener);
|
|
5139
5490
|
}
|
|
5140
5491
|
}
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5492
|
+
function start$1(s, cfg) {
|
|
5493
|
+
if (s.running) {
|
|
5494
|
+
s.logger.warn('Fixer is already running. Call stop() first.');
|
|
5495
|
+
return;
|
|
5496
|
+
}
|
|
5497
|
+
s.iframe = cfg.iframe;
|
|
5498
|
+
s.config = cfg;
|
|
5499
|
+
s.running = true;
|
|
5500
|
+
s.logger.configure({ enabled: !!cfg.debug });
|
|
5501
|
+
s.logger.log('Started');
|
|
5502
|
+
initialize(s);
|
|
5148
5503
|
}
|
|
5149
|
-
function
|
|
5150
|
-
|
|
5504
|
+
function stop$1(s) {
|
|
5505
|
+
if (!s.running)
|
|
5506
|
+
return;
|
|
5507
|
+
s.viewportReplacer.stop();
|
|
5508
|
+
s.heightObserver.stop();
|
|
5509
|
+
s.navigationBlocker.stop();
|
|
5510
|
+
if (s.iframe && s.loadListener) {
|
|
5511
|
+
s.iframe.removeEventListener('load', s.loadListener);
|
|
5512
|
+
s.loadListener = null;
|
|
5513
|
+
}
|
|
5514
|
+
s.iframe = null;
|
|
5515
|
+
s.config = null;
|
|
5516
|
+
s.running = false;
|
|
5517
|
+
s.logger.log('Stopped');
|
|
5518
|
+
}
|
|
5519
|
+
async function recalculate$1(s) {
|
|
5520
|
+
if (!s.running) {
|
|
5521
|
+
s.logger.warn('Fixer is not running.');
|
|
5522
|
+
return;
|
|
5523
|
+
}
|
|
5524
|
+
s.logger.log('Recalculating...');
|
|
5525
|
+
await process(s);
|
|
5151
5526
|
}
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
function enableNavigationBlocking$2() {
|
|
5156
|
-
if (!running$1) {
|
|
5157
|
-
logger.warn('Fixer is not running. Call start() first.');
|
|
5527
|
+
function updateConfig(s, cfg) {
|
|
5528
|
+
if (!s.running || !s.config) {
|
|
5529
|
+
s.logger.warn('Fixer is not running.');
|
|
5158
5530
|
return;
|
|
5159
5531
|
}
|
|
5160
|
-
|
|
5532
|
+
s.config = { ...s.config, ...cfg };
|
|
5533
|
+
s.viewportReplacer.updateConfig(cfg);
|
|
5534
|
+
s.logger.log('Config updated');
|
|
5535
|
+
}
|
|
5536
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
5537
|
+
function createOrchestrator() {
|
|
5538
|
+
const s = {
|
|
5539
|
+
logger: createLogger({ enabled: false, prefix: 'IframeFixer' }),
|
|
5540
|
+
viewportReplacer: createViewportProcessor(),
|
|
5541
|
+
navigationBlocker: createNavigationBlocker(),
|
|
5542
|
+
heightObserver: createHeightObserver(),
|
|
5543
|
+
iframe: null,
|
|
5544
|
+
config: null,
|
|
5545
|
+
running: false,
|
|
5546
|
+
loadListener: null,
|
|
5547
|
+
};
|
|
5548
|
+
return {
|
|
5549
|
+
start: (cfg) => start$1(s, cfg),
|
|
5550
|
+
stop: () => stop$1(s),
|
|
5551
|
+
recalculate: () => recalculate$1(s),
|
|
5552
|
+
updateConfig: (cfg) => updateConfig(s, cfg),
|
|
5553
|
+
enableNavigationBlocking: () => s.navigationBlocker.enable(),
|
|
5554
|
+
enableNavigationBlockingMessage: () => s.navigationBlocker.enableMessage(),
|
|
5555
|
+
disableNavigationBlocking: () => s.navigationBlocker.disable(),
|
|
5556
|
+
disableNavigationBlockingMessage: () => s.navigationBlocker.disableMessage(),
|
|
5557
|
+
isRunning: () => s.running,
|
|
5558
|
+
getStateInfo: () => ({
|
|
5559
|
+
isRunning: s.running,
|
|
5560
|
+
hasIframe: !!s.iframe,
|
|
5561
|
+
hasConfig: !!s.config,
|
|
5562
|
+
hasNavigationBlocker: s.navigationBlocker.isRunning(),
|
|
5563
|
+
hasHeightObserver: s.heightObserver.isRunning(),
|
|
5564
|
+
viewportReplacerRunning: s.viewportReplacer.isRunning(),
|
|
5565
|
+
}),
|
|
5566
|
+
};
|
|
5161
5567
|
}
|
|
5162
5568
|
|
|
5163
5569
|
/**
|
|
5164
|
-
* Iframe Helper
|
|
5165
|
-
*
|
|
5570
|
+
* Iframe Helper — factory entry point.
|
|
5571
|
+
*
|
|
5572
|
+
* Each call to `createIframeHelper()` returns a fully isolated instance
|
|
5573
|
+
* with its own processor state. Use one instance per iframe.
|
|
5166
5574
|
*/
|
|
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.');
|
|
5575
|
+
// ── Module-level functions ────────────────────────────────────────────────────
|
|
5576
|
+
function start(s, config) {
|
|
5577
|
+
if (s.running) {
|
|
5578
|
+
console.warn('[IframeHelper] Already running. Call stop() first.');
|
|
5177
5579
|
return;
|
|
5178
5580
|
}
|
|
5179
|
-
start
|
|
5180
|
-
running = true;
|
|
5581
|
+
s.fixer.start(config);
|
|
5582
|
+
s.running = true;
|
|
5181
5583
|
}
|
|
5182
|
-
function stop
|
|
5183
|
-
if (!running)
|
|
5584
|
+
function stop(s) {
|
|
5585
|
+
if (!s.running)
|
|
5184
5586
|
return;
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
running = false;
|
|
5587
|
+
s.fixer.stop();
|
|
5588
|
+
s.running = false;
|
|
5188
5589
|
}
|
|
5189
|
-
function
|
|
5190
|
-
if (!running) {
|
|
5191
|
-
console.warn('[
|
|
5590
|
+
async function recalculate(s) {
|
|
5591
|
+
if (!s.running) {
|
|
5592
|
+
console.warn('[IframeHelper] Not running. Call start() first.');
|
|
5192
5593
|
return;
|
|
5193
5594
|
}
|
|
5194
|
-
|
|
5195
|
-
}
|
|
5196
|
-
|
|
5197
|
-
function start(config) {
|
|
5198
|
-
start$1(config);
|
|
5595
|
+
await s.fixer.recalculate();
|
|
5199
5596
|
}
|
|
5200
|
-
function
|
|
5201
|
-
|
|
5597
|
+
function enableNavigationBlocking(s) {
|
|
5598
|
+
if (!s.running) {
|
|
5599
|
+
console.warn('[IframeHelper] Not running. Call start() first.');
|
|
5600
|
+
return;
|
|
5601
|
+
}
|
|
5602
|
+
s.fixer.enableNavigationBlocking();
|
|
5202
5603
|
}
|
|
5203
|
-
|
|
5204
|
-
|
|
5604
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
5605
|
+
function createIframeHelper() {
|
|
5606
|
+
const s = {
|
|
5607
|
+
fixer: createOrchestrator(),
|
|
5608
|
+
running: false,
|
|
5609
|
+
};
|
|
5610
|
+
return {
|
|
5611
|
+
start: (config) => start(s, config),
|
|
5612
|
+
stop: () => stop(s),
|
|
5613
|
+
recalculate: () => recalculate(s),
|
|
5614
|
+
enableNavigationBlocking: () => enableNavigationBlocking(s),
|
|
5615
|
+
isRunning: () => s.running,
|
|
5616
|
+
};
|
|
5205
5617
|
}
|
|
5206
5618
|
|
|
5619
|
+
const iframeHelper = createIframeHelper();
|
|
5207
5620
|
function useVizLiveRender() {
|
|
5208
5621
|
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
5209
5622
|
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
5210
5623
|
const wrapperWidth = useHeatmapVizRectContext((s) => s.wrapperWidth);
|
|
5211
|
-
const
|
|
5212
|
-
const htmlContent =
|
|
5213
|
-
const targetUrl =
|
|
5624
|
+
const setIsDomLoaded = useHeatmapVizContext((s) => s.setIsDomLoaded);
|
|
5625
|
+
const htmlContent = useHeatmapLiveContext((s) => s.htmlContent);
|
|
5626
|
+
const targetUrl = useHeatmapLiveContext((s) => s.targetUrl);
|
|
5214
5627
|
const deviceType = useHeatmapSettingContext((s) => s.deviceType);
|
|
5215
|
-
const renderMode =
|
|
5216
|
-
const storefrontPassword =
|
|
5217
|
-
|
|
5628
|
+
const renderMode = useHeatmapLiveContext((s) => s.renderMode);
|
|
5629
|
+
const storefrontPassword = useHeatmapLiveContext((s) => s.storefrontPassword);
|
|
5630
|
+
useHeatmapViewportByDevice();
|
|
5218
5631
|
const { iframeRef, isReady } = useVizLiveIframeMsg();
|
|
5219
5632
|
// Handle iframe rendering based on mode
|
|
5220
5633
|
useEffect(() => {
|
|
@@ -5254,10 +5667,10 @@ function useVizLiveRender() {
|
|
|
5254
5667
|
const hasContent = (renderMode === 'portal' && targetUrl) || (renderMode === 'inline' && htmlContent);
|
|
5255
5668
|
if (!iframe || !hasContent)
|
|
5256
5669
|
return;
|
|
5257
|
-
|
|
5258
|
-
|
|
5670
|
+
setIsDomLoaded(true);
|
|
5671
|
+
startIframe$1(iframe, deviceType, { width: wrapperWidth, height: wrapperHeight }, (height) => {
|
|
5259
5672
|
height && setIframeHeight(height);
|
|
5260
|
-
|
|
5673
|
+
setIsDomLoaded(true);
|
|
5261
5674
|
});
|
|
5262
5675
|
return () => { };
|
|
5263
5676
|
}, [
|
|
@@ -5269,7 +5682,7 @@ function useVizLiveRender() {
|
|
|
5269
5682
|
targetUrl,
|
|
5270
5683
|
htmlContent,
|
|
5271
5684
|
iframeRef,
|
|
5272
|
-
|
|
5685
|
+
setIsDomLoaded,
|
|
5273
5686
|
setIframeHeight,
|
|
5274
5687
|
]);
|
|
5275
5688
|
return {
|
|
@@ -5288,9 +5701,9 @@ function buildPortalUrl(targetUrl, storefrontPassword) {
|
|
|
5288
5701
|
const portalServiceUrl = getPortalServiceUrl();
|
|
5289
5702
|
return `${portalServiceUrl}/?${params.toString()}`;
|
|
5290
5703
|
}
|
|
5291
|
-
function
|
|
5292
|
-
stop();
|
|
5293
|
-
start({
|
|
5704
|
+
function startIframe$1(iframe, deviceType = EDeviceType.Desktop, rect, onSuccess) {
|
|
5705
|
+
iframeHelper.stop();
|
|
5706
|
+
iframeHelper.start({
|
|
5294
5707
|
deviceType: deviceType,
|
|
5295
5708
|
targetWidth: rect.width,
|
|
5296
5709
|
targetHeight: rect.height,
|
|
@@ -5301,12 +5714,115 @@ function initIframeHelper$1(iframe, deviceType = EDeviceType.Desktop, rect, onSu
|
|
|
5301
5714
|
},
|
|
5302
5715
|
});
|
|
5303
5716
|
// fixer.recalculate();
|
|
5304
|
-
enableNavigationBlocking();
|
|
5717
|
+
iframeHelper.enableNavigationBlocking();
|
|
5718
|
+
}
|
|
5719
|
+
|
|
5720
|
+
const DEFAULT_CONFIG = {
|
|
5721
|
+
dbName: 'gx-viz-html-cache',
|
|
5722
|
+
maxEntries: 20,
|
|
5723
|
+
ttlMs: 24 * 60 * 60 * 1000, // 24 hours
|
|
5724
|
+
cacheVersion: 3.0,
|
|
5725
|
+
};
|
|
5726
|
+
let _config = { ...DEFAULT_CONFIG };
|
|
5727
|
+
function getHtmlCacheConfig() {
|
|
5728
|
+
return _config;
|
|
5729
|
+
}
|
|
5730
|
+
/** Build a full cache key that includes the cache version to handle invalidation. */
|
|
5731
|
+
function buildCacheKey(baseKey, shortCircuitStrategy = 0) {
|
|
5732
|
+
return `v${_config.cacheVersion}:${baseKey}:${shortCircuitStrategy}`;
|
|
5733
|
+
}
|
|
5734
|
+
|
|
5735
|
+
const STORE_NAME = 'entries';
|
|
5736
|
+
const IDB_SCHEMA_VERSION = 1;
|
|
5737
|
+
function openDb() {
|
|
5738
|
+
const { dbName } = getHtmlCacheConfig();
|
|
5739
|
+
return new Promise((resolve, reject) => {
|
|
5740
|
+
const req = indexedDB.open(dbName, IDB_SCHEMA_VERSION);
|
|
5741
|
+
req.onupgradeneeded = () => {
|
|
5742
|
+
const db = req.result;
|
|
5743
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
5744
|
+
const store = db.createObjectStore(STORE_NAME, { keyPath: 'key' });
|
|
5745
|
+
store.createIndex('timestamp', 'timestamp');
|
|
5746
|
+
}
|
|
5747
|
+
};
|
|
5748
|
+
req.onsuccess = () => resolve(req.result);
|
|
5749
|
+
req.onerror = () => reject(req.error);
|
|
5750
|
+
});
|
|
5305
5751
|
}
|
|
5752
|
+
async function evict(db) {
|
|
5753
|
+
const { maxEntries } = getHtmlCacheConfig();
|
|
5754
|
+
return new Promise((resolve) => {
|
|
5755
|
+
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
5756
|
+
const store = tx.objectStore(STORE_NAME);
|
|
5757
|
+
const countReq = store.count();
|
|
5758
|
+
countReq.onsuccess = () => {
|
|
5759
|
+
if (countReq.result <= maxEntries) {
|
|
5760
|
+
resolve();
|
|
5761
|
+
return;
|
|
5762
|
+
}
|
|
5763
|
+
const idx = store.index('timestamp');
|
|
5764
|
+
const cursorReq = idx.openCursor();
|
|
5765
|
+
let toDelete = countReq.result - maxEntries;
|
|
5766
|
+
cursorReq.onsuccess = () => {
|
|
5767
|
+
const cursor = cursorReq.result;
|
|
5768
|
+
if (cursor && toDelete > 0) {
|
|
5769
|
+
cursor.delete();
|
|
5770
|
+
toDelete--;
|
|
5771
|
+
cursor.continue();
|
|
5772
|
+
}
|
|
5773
|
+
else {
|
|
5774
|
+
resolve();
|
|
5775
|
+
}
|
|
5776
|
+
};
|
|
5777
|
+
cursorReq.onerror = () => resolve();
|
|
5778
|
+
};
|
|
5779
|
+
countReq.onerror = () => resolve();
|
|
5780
|
+
});
|
|
5781
|
+
}
|
|
5782
|
+
const htmlCache = {
|
|
5783
|
+
async get(key) {
|
|
5784
|
+
try {
|
|
5785
|
+
const { ttlMs } = getHtmlCacheConfig();
|
|
5786
|
+
const db = await openDb();
|
|
5787
|
+
return new Promise((resolve) => {
|
|
5788
|
+
const tx = db.transaction(STORE_NAME, 'readonly');
|
|
5789
|
+
const req = tx.objectStore(STORE_NAME).get(key);
|
|
5790
|
+
req.onsuccess = () => {
|
|
5791
|
+
const entry = req.result;
|
|
5792
|
+
if (!entry || Date.now() - entry.timestamp > ttlMs) {
|
|
5793
|
+
resolve(null);
|
|
5794
|
+
}
|
|
5795
|
+
else {
|
|
5796
|
+
resolve(entry);
|
|
5797
|
+
}
|
|
5798
|
+
};
|
|
5799
|
+
req.onerror = () => resolve(null);
|
|
5800
|
+
});
|
|
5801
|
+
}
|
|
5802
|
+
catch {
|
|
5803
|
+
return null;
|
|
5804
|
+
}
|
|
5805
|
+
},
|
|
5806
|
+
async set(entry) {
|
|
5807
|
+
try {
|
|
5808
|
+
const db = await openDb();
|
|
5809
|
+
await new Promise((resolve, reject) => {
|
|
5810
|
+
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
5811
|
+
tx.objectStore(STORE_NAME).put(entry);
|
|
5812
|
+
tx.oncomplete = () => resolve();
|
|
5813
|
+
tx.onerror = () => reject(tx.error);
|
|
5814
|
+
});
|
|
5815
|
+
await evict(db);
|
|
5816
|
+
}
|
|
5817
|
+
catch {
|
|
5818
|
+
// ignore cache write errors
|
|
5819
|
+
}
|
|
5820
|
+
},
|
|
5821
|
+
};
|
|
5306
5822
|
|
|
5307
5823
|
const CANVAS_ID = 'clarity-heatmap-canvas';
|
|
5308
5824
|
const ATTENTION_HUES = [240, 210, 180, 150, 120, 90, 60, 30, 0];
|
|
5309
|
-
const CANVAS_MAX_HEIGHT = 65535;
|
|
5825
|
+
const CANVAS_MAX_HEIGHT$1 = 65535;
|
|
5310
5826
|
const Z_INDEX = 2147483647;
|
|
5311
5827
|
const DEFAULT_IFRAME_ID = 'clarity-snapshot-heatmap-iframe';
|
|
5312
5828
|
const SECONDARY_IFRAME_ID = 'clarity-snapshot-heatmap-iframe-secondary';
|
|
@@ -5371,7 +5887,7 @@ class AttentionMapRenderer {
|
|
|
5371
5887
|
const body = doc.body;
|
|
5372
5888
|
const de = doc.documentElement;
|
|
5373
5889
|
const contentHeight = Math.max(body.scrollHeight, body.offsetHeight, de.clientHeight, de.scrollHeight, de.offsetHeight);
|
|
5374
|
-
canvas.height = Math.min(contentHeight, CANVAS_MAX_HEIGHT);
|
|
5890
|
+
canvas.height = Math.min(contentHeight, CANVAS_MAX_HEIGHT$1);
|
|
5375
5891
|
canvas.style.top = '0px';
|
|
5376
5892
|
if (canvas.width <= 0 || canvas.height <= 0 || !this.attentionMapData)
|
|
5377
5893
|
return;
|
|
@@ -5393,7 +5909,7 @@ class AttentionMapRenderer {
|
|
|
5393
5909
|
const body = doc.body;
|
|
5394
5910
|
const de = doc.documentElement;
|
|
5395
5911
|
const contentHeight = Math.max(body.scrollHeight, body.offsetHeight, de.clientHeight, de.scrollHeight, de.offsetHeight);
|
|
5396
|
-
canvas.height = Math.min(contentHeight, CANVAS_MAX_HEIGHT);
|
|
5912
|
+
canvas.height = Math.min(contentHeight, CANVAS_MAX_HEIGHT$1);
|
|
5397
5913
|
canvas.style.top = '0px';
|
|
5398
5914
|
let iframeEl = document.getElementById(this.heatmapIframeId);
|
|
5399
5915
|
if (!iframeEl) {
|
|
@@ -5409,9 +5925,9 @@ class AttentionMapRenderer {
|
|
|
5409
5925
|
for (const point of this.attentionMapData) {
|
|
5410
5926
|
const timePercent = Math.floor((100 * point.cumulativeTime) / maxCumulativeTime);
|
|
5411
5927
|
const colorIndex = timePercent <= 5 ? 0 : Math.min(Math.ceil(timePercent / 10), 8);
|
|
5412
|
-
if (point.
|
|
5928
|
+
if (point.scrollReachY / 100 <= 1) {
|
|
5413
5929
|
point.colorIndex = colorIndex;
|
|
5414
|
-
colorStops[point.
|
|
5930
|
+
colorStops[point.scrollReachY] = colorIndex;
|
|
5415
5931
|
}
|
|
5416
5932
|
}
|
|
5417
5933
|
// Smooth the top portion based on the fold/viewport ratio
|
|
@@ -5442,7 +5958,7 @@ class AttentionMapRenderer {
|
|
|
5442
5958
|
buildAggregatedData = (doc, contentHeight, iframeEl) => {
|
|
5443
5959
|
const elementCache = new Map();
|
|
5444
5960
|
this.attentionMapData = Array.from({ length: 100 }, (_, i) => ({
|
|
5445
|
-
|
|
5961
|
+
scrollReachY: i,
|
|
5446
5962
|
cumulativeTime: 0,
|
|
5447
5963
|
colorIndex: 0,
|
|
5448
5964
|
}));
|
|
@@ -5512,32 +6028,490 @@ class AttentionMapRenderer {
|
|
|
5512
6028
|
};
|
|
5513
6029
|
}
|
|
5514
6030
|
|
|
6031
|
+
const CANVAS_MAX_HEIGHT = 65535;
|
|
6032
|
+
const REDRAW_INTERVAL = 30;
|
|
6033
|
+
class ScrollBucketRenderer {
|
|
6034
|
+
heatmap;
|
|
6035
|
+
lastData = null;
|
|
6036
|
+
dimensionsListener = null;
|
|
6037
|
+
redrawTimeout = null;
|
|
6038
|
+
constructor(heatmap) {
|
|
6039
|
+
this.heatmap = heatmap;
|
|
6040
|
+
this.attachDimensionsListener();
|
|
6041
|
+
}
|
|
6042
|
+
redraw = () => {
|
|
6043
|
+
if (!this.lastData)
|
|
6044
|
+
return;
|
|
6045
|
+
if (this.redrawTimeout)
|
|
6046
|
+
clearTimeout(this.redrawTimeout);
|
|
6047
|
+
this.redrawTimeout = setTimeout(() => this.draw(this.lastData), REDRAW_INTERVAL);
|
|
6048
|
+
};
|
|
6049
|
+
attachDimensionsListener = () => {
|
|
6050
|
+
this.dimensionsListener = () => this.redraw();
|
|
6051
|
+
window.addEventListener('iframe-dimensions-applied', this.dimensionsListener);
|
|
6052
|
+
};
|
|
6053
|
+
detachDimensionsListener = () => {
|
|
6054
|
+
if (this.dimensionsListener) {
|
|
6055
|
+
window.removeEventListener('iframe-dimensions-applied', this.dimensionsListener);
|
|
6056
|
+
this.dimensionsListener = null;
|
|
6057
|
+
}
|
|
6058
|
+
if (this.redrawTimeout) {
|
|
6059
|
+
clearTimeout(this.redrawTimeout);
|
|
6060
|
+
this.redrawTimeout = null;
|
|
6061
|
+
}
|
|
6062
|
+
};
|
|
6063
|
+
reset = () => {
|
|
6064
|
+
// no-op: canvas state is managed by the shared heatmap instance
|
|
6065
|
+
};
|
|
6066
|
+
clear = () => {
|
|
6067
|
+
this.lastData = null;
|
|
6068
|
+
this.detachDimensionsListener();
|
|
6069
|
+
};
|
|
6070
|
+
/**
|
|
6071
|
+
* Render discrete color bands for scrollRevenue / scrollAttention data.
|
|
6072
|
+
* Uses the shared heatmap canvas (same as HeatmapHelper.scroll()) to avoid
|
|
6073
|
+
* duplicate portal canvas management.
|
|
6074
|
+
* Stores data so the canvas can be redrawn when iframe height changes.
|
|
6075
|
+
*/
|
|
6076
|
+
renderBucket = async (data) => {
|
|
6077
|
+
if (!this.heatmap)
|
|
6078
|
+
return;
|
|
6079
|
+
this.attachDimensionsListener();
|
|
6080
|
+
this.lastData = data;
|
|
6081
|
+
await this.heatmap.waitForDialogs();
|
|
6082
|
+
this.draw(data);
|
|
6083
|
+
};
|
|
6084
|
+
draw = (data) => {
|
|
6085
|
+
if (!this.heatmap)
|
|
6086
|
+
return;
|
|
6087
|
+
const canvas = this.heatmap.overlay();
|
|
6088
|
+
if (!canvas)
|
|
6089
|
+
return;
|
|
6090
|
+
const context = canvas.getContext('2d');
|
|
6091
|
+
const doc = this.heatmap.state.window.document;
|
|
6092
|
+
const body = doc.body;
|
|
6093
|
+
const de = doc.documentElement;
|
|
6094
|
+
const height = Math.max(body.scrollHeight, body.offsetHeight, de.clientHeight, de.scrollHeight, de.offsetHeight);
|
|
6095
|
+
// Same canvas setup as HeatmapHelper.scroll(): full content height, top=0
|
|
6096
|
+
canvas.height = Math.min(height, CANVAS_MAX_HEIGHT);
|
|
6097
|
+
canvas.style.top = '0px';
|
|
6098
|
+
if (canvas.width <= 0 || canvas.height <= 0)
|
|
6099
|
+
return;
|
|
6100
|
+
const buckets = this.mapToBuckets(data);
|
|
6101
|
+
if (buckets.length === 0)
|
|
6102
|
+
return;
|
|
6103
|
+
const maxPercent = Math.max(...buckets.map((b) => b.percent));
|
|
6104
|
+
if (maxPercent <= 0)
|
|
6105
|
+
return;
|
|
6106
|
+
const gradient = context.createLinearGradient(0, 0, 0, canvas.height);
|
|
6107
|
+
for (const bucket of buckets) {
|
|
6108
|
+
const stopMid = (bucket.startY + bucket.endY) / 2 / 100;
|
|
6109
|
+
// Same hue direction as HeatmapHelper.scroll() (MaxHue = 240):
|
|
6110
|
+
// high percent → hue 0 (red/hot), low percent → hue 240 (blue/cold)
|
|
6111
|
+
const hue = (1 - bucket.percent / maxPercent) * 240;
|
|
6112
|
+
const color = `hsla(${Math.round(hue)}, 100%, 50%, 0.6)`;
|
|
6113
|
+
gradient.addColorStop(stopMid, color);
|
|
6114
|
+
}
|
|
6115
|
+
context.fillStyle = gradient;
|
|
6116
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
6117
|
+
};
|
|
6118
|
+
/**
|
|
6119
|
+
* Convert flat position markers → structured buckets with startY/endY.
|
|
6120
|
+
*
|
|
6121
|
+
* Input positions: [0, 5, 10, 15, ..., 95]
|
|
6122
|
+
* position=0 → { startY: 0, endY: 5 } (special start marker)
|
|
6123
|
+
* position=10 → { startY: 5, endY: 10 }
|
|
6124
|
+
* position=15 → { startY: 10, endY: 15 }
|
|
6125
|
+
*/
|
|
6126
|
+
mapToBuckets = (data) => {
|
|
6127
|
+
const sorted = [...data].sort((a, b) => a.position - b.position);
|
|
6128
|
+
const buckets = [];
|
|
6129
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
6130
|
+
const current = sorted[i];
|
|
6131
|
+
let startY;
|
|
6132
|
+
let endY;
|
|
6133
|
+
if (current.position === 0) {
|
|
6134
|
+
// position=0 is the start marker → range 0 to next position
|
|
6135
|
+
const next = sorted[i + 1];
|
|
6136
|
+
if (!next)
|
|
6137
|
+
continue;
|
|
6138
|
+
startY = 0;
|
|
6139
|
+
endY = next.position;
|
|
6140
|
+
}
|
|
6141
|
+
else {
|
|
6142
|
+
// Each non-zero position is the END of its bucket; start = previous position
|
|
6143
|
+
const prev = sorted[i - 1];
|
|
6144
|
+
startY = prev ? prev.position : 0;
|
|
6145
|
+
endY = current.position;
|
|
6146
|
+
}
|
|
6147
|
+
buckets.push({ startY, endY, value: current.value, percent: current.percent });
|
|
6148
|
+
}
|
|
6149
|
+
return buckets;
|
|
6150
|
+
};
|
|
6151
|
+
}
|
|
6152
|
+
|
|
6153
|
+
var Event;
|
|
6154
|
+
(function (Event) {
|
|
6155
|
+
/* Data */
|
|
6156
|
+
Event[Event["Metric"] = 0] = "Metric";
|
|
6157
|
+
Event[Event["Dimension"] = 1] = "Dimension";
|
|
6158
|
+
Event[Event["Upload"] = 2] = "Upload";
|
|
6159
|
+
Event[Event["Upgrade"] = 3] = "Upgrade";
|
|
6160
|
+
Event[Event["Baseline"] = 4] = "Baseline";
|
|
6161
|
+
Event[Event["Discover"] = 5] = "Discover";
|
|
6162
|
+
Event[Event["Mutation"] = 6] = "Mutation";
|
|
6163
|
+
Event[Event["Region"] = 7] = "Region";
|
|
6164
|
+
Event[Event["Document"] = 8] = "Document";
|
|
6165
|
+
Event[Event["Click"] = 9] = "Click";
|
|
6166
|
+
Event[Event["Scroll"] = 10] = "Scroll";
|
|
6167
|
+
Event[Event["Resize"] = 11] = "Resize";
|
|
6168
|
+
Event[Event["MouseMove"] = 12] = "MouseMove";
|
|
6169
|
+
Event[Event["MouseDown"] = 13] = "MouseDown";
|
|
6170
|
+
Event[Event["MouseUp"] = 14] = "MouseUp";
|
|
6171
|
+
Event[Event["MouseWheel"] = 15] = "MouseWheel";
|
|
6172
|
+
Event[Event["DoubleClick"] = 16] = "DoubleClick";
|
|
6173
|
+
Event[Event["TouchStart"] = 17] = "TouchStart";
|
|
6174
|
+
Event[Event["TouchEnd"] = 18] = "TouchEnd";
|
|
6175
|
+
Event[Event["TouchMove"] = 19] = "TouchMove";
|
|
6176
|
+
Event[Event["TouchCancel"] = 20] = "TouchCancel";
|
|
6177
|
+
Event[Event["Selection"] = 21] = "Selection";
|
|
6178
|
+
Event[Event["Timeline"] = 22] = "Timeline";
|
|
6179
|
+
Event[Event["Page"] = 23] = "Page";
|
|
6180
|
+
Event[Event["Custom"] = 24] = "Custom";
|
|
6181
|
+
Event[Event["Ping"] = 25] = "Ping";
|
|
6182
|
+
Event[Event["Unload"] = 26] = "Unload";
|
|
6183
|
+
Event[Event["Input"] = 27] = "Input";
|
|
6184
|
+
Event[Event["Visibility"] = 28] = "Visibility";
|
|
6185
|
+
Event[Event["Navigation"] = 29] = "Navigation";
|
|
6186
|
+
/**
|
|
6187
|
+
* @deprecated No longer support Network Connection
|
|
6188
|
+
*/
|
|
6189
|
+
Event[Event["Connection"] = 30] = "Connection";
|
|
6190
|
+
Event[Event["ScriptError"] = 31] = "ScriptError";
|
|
6191
|
+
/**
|
|
6192
|
+
* @deprecated No longer support Image Error
|
|
6193
|
+
*/
|
|
6194
|
+
Event[Event["ImageError"] = 32] = "ImageError";
|
|
6195
|
+
Event[Event["Log"] = 33] = "Log";
|
|
6196
|
+
Event[Event["Variable"] = 34] = "Variable";
|
|
6197
|
+
Event[Event["Limit"] = 35] = "Limit";
|
|
6198
|
+
Event[Event["Summary"] = 36] = "Summary";
|
|
6199
|
+
/**
|
|
6200
|
+
* @deprecated No longer support Box event
|
|
6201
|
+
*/
|
|
6202
|
+
Event[Event["Box"] = 37] = "Box";
|
|
6203
|
+
Event[Event["Clipboard"] = 38] = "Clipboard";
|
|
6204
|
+
Event[Event["Submit"] = 39] = "Submit";
|
|
6205
|
+
Event[Event["Extract"] = 40] = "Extract";
|
|
6206
|
+
Event[Event["Fraud"] = 41] = "Fraud";
|
|
6207
|
+
Event[Event["Change"] = 42] = "Change";
|
|
6208
|
+
Event[Event["Snapshot"] = 43] = "Snapshot";
|
|
6209
|
+
Event[Event["Animation"] = 44] = "Animation";
|
|
6210
|
+
Event[Event["StyleSheetAdoption"] = 45] = "StyleSheetAdoption";
|
|
6211
|
+
Event[Event["StyleSheetUpdate"] = 46] = "StyleSheetUpdate";
|
|
6212
|
+
Event[Event["Consent"] = 47] = "Consent";
|
|
6213
|
+
Event[Event["ContextMenu"] = 48] = "ContextMenu";
|
|
6214
|
+
// 49 is reserved for internal use
|
|
6215
|
+
Event[Event["Focus"] = 50] = "Focus";
|
|
6216
|
+
Event[Event["CustomElement"] = 51] = "CustomElement";
|
|
6217
|
+
Event[Event["Chat"] = 52] = "Chat";
|
|
6218
|
+
// Apps specific events
|
|
6219
|
+
Event[Event["WebViewDiscover"] = 100] = "WebViewDiscover";
|
|
6220
|
+
Event[Event["WebViewMutation"] = 101] = "WebViewMutation";
|
|
6221
|
+
Event[Event["MutationError"] = 102] = "MutationError";
|
|
6222
|
+
Event[Event["FragmentVisibility"] = 103] = "FragmentVisibility";
|
|
6223
|
+
Event[Event["Keystrokes"] = 104] = "Keystrokes";
|
|
6224
|
+
Event[Event["BackGesture"] = 105] = "BackGesture";
|
|
6225
|
+
Event[Event["WebViewStatus"] = 106] = "WebViewStatus";
|
|
6226
|
+
Event[Event["AppInstallReferrer"] = 107] = "AppInstallReferrer";
|
|
6227
|
+
// 200-300 reserved for internal use
|
|
6228
|
+
})(Event || (Event = {}));
|
|
6229
|
+
|
|
6230
|
+
var Constant;
|
|
6231
|
+
(function (Constant) {
|
|
6232
|
+
Constant["Empty"] = "";
|
|
6233
|
+
Constant["SvgPrefix"] = "svg:";
|
|
6234
|
+
Constant["DataPrefix"] = "data:";
|
|
6235
|
+
Constant["IFramePrefix"] = "iframe:";
|
|
6236
|
+
Constant["SvgNamespace"] = "http://www.w3.org/2000/svg";
|
|
6237
|
+
Constant["Id"] = "id";
|
|
6238
|
+
Constant["Class"] = "class";
|
|
6239
|
+
Constant["Style"] = "style";
|
|
6240
|
+
Constant["Href"] = "href";
|
|
6241
|
+
Constant["Src"] = "src";
|
|
6242
|
+
Constant["Srcset"] = "srcset";
|
|
6243
|
+
Constant["Hash"] = "#";
|
|
6244
|
+
Constant["Dot"] = ".";
|
|
6245
|
+
Constant["Separator"] = ">";
|
|
6246
|
+
Constant["Tilde"] = "~";
|
|
6247
|
+
Constant["Bang"] = "!";
|
|
6248
|
+
Constant["Period"] = ".";
|
|
6249
|
+
Constant["Comma"] = ",";
|
|
6250
|
+
Constant["DataAttribute"] = "data-";
|
|
6251
|
+
Constant["MaskData"] = "data-clarity-mask";
|
|
6252
|
+
Constant["UnmaskData"] = "data-clarity-unmask";
|
|
6253
|
+
Constant["RegionData"] = "data-clarity-region";
|
|
6254
|
+
Constant["GXDialogModal"] = "gx-dialog-modal";
|
|
6255
|
+
Constant["Type"] = "type";
|
|
6256
|
+
Constant["Submit"] = "submit";
|
|
6257
|
+
Constant["Name"] = "name";
|
|
6258
|
+
Constant["Base"] = "*B";
|
|
6259
|
+
Constant["SameOrigin"] = "*O";
|
|
6260
|
+
Constant["Object"] = "object";
|
|
6261
|
+
Constant["Function"] = "function";
|
|
6262
|
+
Constant["StyleTag"] = "STYLE";
|
|
6263
|
+
Constant["LinkTag"] = "LINK";
|
|
6264
|
+
Constant["InputTag"] = "INPUT";
|
|
6265
|
+
Constant["IFrameTag"] = "IFRAME";
|
|
6266
|
+
Constant["ImageTag"] = "IMG";
|
|
6267
|
+
Constant["TitleTag"] = "TITLE";
|
|
6268
|
+
Constant["BodyTag"] = "BODY";
|
|
6269
|
+
Constant["SvgTag"] = "svg:svg";
|
|
6270
|
+
Constant["BaseTag"] = "BASE";
|
|
6271
|
+
Constant["NativeCode"] = "[native code]";
|
|
6272
|
+
Constant["DocumentTag"] = "*D";
|
|
6273
|
+
Constant["ShadowDomTag"] = "*S";
|
|
6274
|
+
Constant["PolyfillShadowDomTag"] = "*P";
|
|
6275
|
+
Constant["TextTag"] = "*T";
|
|
6276
|
+
Constant["SuspendMutationTag"] = "*M";
|
|
6277
|
+
Constant["ChildList"] = "childList";
|
|
6278
|
+
Constant["Attributes"] = "attributes";
|
|
6279
|
+
Constant["CharacterData"] = "characterData";
|
|
6280
|
+
Constant["Throttle"] = "throttle";
|
|
6281
|
+
Constant["LoadEvent"] = "load";
|
|
6282
|
+
Constant["Pixel"] = "px";
|
|
6283
|
+
Constant["BorderBox"] = "border-box";
|
|
6284
|
+
Constant["Value"] = "value";
|
|
6285
|
+
Constant["MutationObserver"] = "MutationObserver";
|
|
6286
|
+
Constant["JsonLD"] = "application/ld+json";
|
|
6287
|
+
Constant["String"] = "string";
|
|
6288
|
+
Constant["Number"] = "number";
|
|
6289
|
+
Constant["Disable"] = "disable";
|
|
6290
|
+
Constant["HTML"] = "HTML";
|
|
6291
|
+
Constant["Property"] = "property";
|
|
6292
|
+
Constant["Content"] = "content";
|
|
6293
|
+
Constant["Generator"] = "generator";
|
|
6294
|
+
Constant["ogType"] = "og:type";
|
|
6295
|
+
Constant["ogTitle"] = "og:title";
|
|
6296
|
+
Constant["SvgStyle"] = "svg:style";
|
|
6297
|
+
Constant["StyleSheet"] = "stylesheet";
|
|
6298
|
+
})(Constant || (Constant = {}));
|
|
6299
|
+
|
|
6300
|
+
var ShortCircuitStrategy;
|
|
6301
|
+
(function (ShortCircuitStrategy) {
|
|
6302
|
+
ShortCircuitStrategy[ShortCircuitStrategy["None"] = 0] = "None";
|
|
6303
|
+
ShortCircuitStrategy[ShortCircuitStrategy["HashFirstTimestamp"] = 1] = "HashFirstTimestamp";
|
|
6304
|
+
ShortCircuitStrategy[ShortCircuitStrategy["HashFirstTimestampPlusBuffer"] = 2] = "HashFirstTimestampPlusBuffer";
|
|
6305
|
+
ShortCircuitStrategy[ShortCircuitStrategy["HashBeforeDeleted"] = 3] = "HashBeforeDeleted";
|
|
6306
|
+
})(ShortCircuitStrategy || (ShortCircuitStrategy = {}));
|
|
6307
|
+
|
|
6308
|
+
function isShadowDomNode(tag) {
|
|
6309
|
+
return tag === Constant.ShadowDomTag || tag === Constant.PolyfillShadowDomTag;
|
|
6310
|
+
}
|
|
6311
|
+
/** Scan the rendered DOM (including nested shadow roots) to collect tag names of shadow host elements. */
|
|
6312
|
+
function collectShadowHostTags(root, tags = new Set()) {
|
|
6313
|
+
root.querySelectorAll('*').forEach((el) => {
|
|
6314
|
+
if (el.shadowRoot) {
|
|
6315
|
+
tags.add(el.tagName);
|
|
6316
|
+
collectShadowHostTags(el.shadowRoot, tags);
|
|
6317
|
+
}
|
|
6318
|
+
});
|
|
6319
|
+
return tags;
|
|
6320
|
+
}
|
|
6321
|
+
/** Build a shadow subtree ID set from all DOM events.
|
|
6322
|
+
* Two-pass cascade to handle any event ordering:
|
|
6323
|
+
* Pass 1 — seed *S/*P node IDs.
|
|
6324
|
+
* Pass 2 — cascade: any node whose parent is in the set is also in the set (repeat until stable). */
|
|
6325
|
+
function buildShadowSubtreeIds(allDomEvents) {
|
|
6326
|
+
const subtreeIds = new Set();
|
|
6327
|
+
for (const e of allDomEvents) {
|
|
6328
|
+
const data = e.data;
|
|
6329
|
+
for (const node of data ?? []) {
|
|
6330
|
+
if (isShadowDomNode(node.tag))
|
|
6331
|
+
subtreeIds.add(node.id);
|
|
6332
|
+
}
|
|
6333
|
+
}
|
|
6334
|
+
let changed = true;
|
|
6335
|
+
while (changed) {
|
|
6336
|
+
changed = false;
|
|
6337
|
+
for (const e of allDomEvents) {
|
|
6338
|
+
const data = e.data;
|
|
6339
|
+
for (const node of data ?? []) {
|
|
6340
|
+
if (!subtreeIds.has(node.id) && subtreeIds.has(node.parent)) {
|
|
6341
|
+
subtreeIds.add(node.id);
|
|
6342
|
+
changed = true;
|
|
6343
|
+
}
|
|
6344
|
+
}
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6347
|
+
return subtreeIds;
|
|
6348
|
+
}
|
|
6349
|
+
function filterShadowNodes(data, subtreeIds, shadowHostTags) {
|
|
6350
|
+
return (data?.filter((node) => {
|
|
6351
|
+
if (isShadowDomNode(node.tag))
|
|
6352
|
+
return true;
|
|
6353
|
+
if (shadowHostTags.has(node.tag ?? ''))
|
|
6354
|
+
return true;
|
|
6355
|
+
return subtreeIds.has(node.id);
|
|
6356
|
+
}) ?? []);
|
|
6357
|
+
}
|
|
6358
|
+
/** Extract shadow DOM nodes from merged.dom (initial Discover event, processed by setup()).
|
|
6359
|
+
* Returns a filtered copy of the dom event, or null if no shadow DOM present. */
|
|
6360
|
+
function extractSpecialDom(dom, events, shadowHostTags) {
|
|
6361
|
+
const mutationEvents = events.filter((e) => e.event === Event.Mutation);
|
|
6362
|
+
const subtreeIds = buildShadowSubtreeIds([dom, ...mutationEvents]);
|
|
6363
|
+
const shadowNodes = filterShadowNodes(dom.data, subtreeIds, shadowHostTags);
|
|
6364
|
+
return shadowNodes.length ? { ...dom, data: shadowNodes } : null;
|
|
6365
|
+
}
|
|
6366
|
+
/** Extract shadow DOM mutations + StyleSheet + CustomElement events from merged.events. */
|
|
6367
|
+
function extractSpecialEvents(events, dom, shadowHostTags) {
|
|
6368
|
+
const mutationEvents = events.filter((e) => e.event === Event.Mutation);
|
|
6369
|
+
const subtreeIds = buildShadowSubtreeIds([dom, ...mutationEvents]);
|
|
6370
|
+
const result = [];
|
|
6371
|
+
for (const e of events) {
|
|
6372
|
+
const ev = e.event;
|
|
6373
|
+
if (ev === Event.StyleSheetAdoption ||
|
|
6374
|
+
ev === Event.StyleSheetUpdate ||
|
|
6375
|
+
ev === Event.CustomElement) {
|
|
6376
|
+
result.push(e);
|
|
6377
|
+
continue;
|
|
6378
|
+
}
|
|
6379
|
+
if (ev === Event.Mutation) {
|
|
6380
|
+
const shadowNodes = filterShadowNodes(e.data, subtreeIds, shadowHostTags);
|
|
6381
|
+
if (shadowNodes.length)
|
|
6382
|
+
result.push({ ...e, data: shadowNodes });
|
|
6383
|
+
}
|
|
6384
|
+
}
|
|
6385
|
+
return result;
|
|
6386
|
+
}
|
|
6387
|
+
|
|
6388
|
+
// utils/retry.ts
|
|
6389
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
6390
|
+
/**
|
|
6391
|
+
* Retry until the callback returns truthy, or throw if the limit is exceeded.
|
|
6392
|
+
*
|
|
6393
|
+
* @example
|
|
6394
|
+
* await retry(() => doc.readyState === 'complete', { timeout: 5000, label: 'DOM ready' });
|
|
6395
|
+
*
|
|
6396
|
+
* @example
|
|
6397
|
+
* const el = await retry(() => document.getElementById('app'), { maxRetries: 20 });
|
|
6398
|
+
*/
|
|
6399
|
+
async function retry(callback, options = {}) {
|
|
6400
|
+
const { interval = 100, label = 'Retry' } = options;
|
|
6401
|
+
const maxRetries = options.timeout != null ? Math.ceil(options.timeout / interval) : (options.maxRetries ?? 50);
|
|
6402
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
6403
|
+
console.log(`🔄 [${label}] attempt ${attempt}/${maxRetries}`);
|
|
6404
|
+
const result = await callback();
|
|
6405
|
+
if (result) {
|
|
6406
|
+
return result;
|
|
6407
|
+
}
|
|
6408
|
+
if (attempt < maxRetries) {
|
|
6409
|
+
await delay(interval);
|
|
6410
|
+
}
|
|
6411
|
+
}
|
|
6412
|
+
const totalMs = maxRetries * interval;
|
|
6413
|
+
throw new Error(`[${label}] Timed out after ${maxRetries} retries (${totalMs}ms)`);
|
|
6414
|
+
}
|
|
6415
|
+
|
|
6416
|
+
const perf$2 = createPerfTimer('Render');
|
|
6417
|
+
const YIELD_INTERVAL_MS = 16;
|
|
6418
|
+
async function yieldToMain() {
|
|
6419
|
+
if ('scheduler' in globalThis && typeof globalThis.scheduler?.yield === 'function') {
|
|
6420
|
+
await globalThis.scheduler.yield();
|
|
6421
|
+
}
|
|
6422
|
+
else {
|
|
6423
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
6426
|
+
const renderLoop = async (ctx, options) => {
|
|
6427
|
+
const { events, useproxy } = options;
|
|
6428
|
+
const shortCircuitStrategy = options.shortCircuitStrategy ?? ShortCircuitStrategy.None;
|
|
6429
|
+
const hash = options.hash ?? null;
|
|
6430
|
+
const t0 = perf$2.mark('RenderLoop start');
|
|
6431
|
+
let lastYield = performance.now();
|
|
6432
|
+
const totalEvents = events.length;
|
|
6433
|
+
for (let i = 0; i < totalEvents; i++) {
|
|
6434
|
+
const entry = events[i];
|
|
6435
|
+
const entryEvent = entry.event;
|
|
6436
|
+
const now = performance.now();
|
|
6437
|
+
if (now - lastYield > YIELD_INTERVAL_MS) {
|
|
6438
|
+
console.log(`[RenderLoop] ${i}/${totalEvents} time:`, now - lastYield);
|
|
6439
|
+
await yieldToMain();
|
|
6440
|
+
lastYield = performance.now();
|
|
6441
|
+
}
|
|
6442
|
+
switch (entryEvent) {
|
|
6443
|
+
case Event.StyleSheetAdoption:
|
|
6444
|
+
case Event.StyleSheetUpdate:
|
|
6445
|
+
ctx.layout.styleChange(entry);
|
|
6446
|
+
break;
|
|
6447
|
+
case Event.CustomElement:
|
|
6448
|
+
ctx.layout.customElement(entry);
|
|
6449
|
+
break;
|
|
6450
|
+
case Event.Mutation: {
|
|
6451
|
+
const domEvent = entry;
|
|
6452
|
+
ctx.renderTime = domEvent.time;
|
|
6453
|
+
if (ctx.shortCircuitRendering(shortCircuitStrategy, domEvent, hash))
|
|
6454
|
+
return;
|
|
6455
|
+
ctx.layout.markup(domEvent, useproxy);
|
|
6456
|
+
break;
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
6459
|
+
}
|
|
6460
|
+
perf$2.measure('RenderLoop', t0);
|
|
6461
|
+
};
|
|
6462
|
+
|
|
5515
6463
|
class GXVisualizer extends Visualizer {
|
|
5516
6464
|
attentionMap;
|
|
6465
|
+
scrollBucketMap;
|
|
5517
6466
|
originalClearmap;
|
|
5518
6467
|
originalSetup;
|
|
6468
|
+
originalScrollmap;
|
|
6469
|
+
originalClickmap;
|
|
5519
6470
|
constructor() {
|
|
5520
6471
|
super();
|
|
5521
6472
|
this.attentionMap = new AttentionMapRenderer(null);
|
|
5522
|
-
|
|
6473
|
+
this.scrollBucketMap = new ScrollBucketRenderer(null);
|
|
5523
6474
|
this.originalSetup = this.setup;
|
|
5524
6475
|
this.originalClearmap = this.clearmap;
|
|
6476
|
+
this.originalScrollmap = this.scrollmap;
|
|
6477
|
+
this.originalClickmap = this.clickmap;
|
|
5525
6478
|
this.clearmap = this.clearmapOverride;
|
|
5526
6479
|
this.setup = this.setupOverride;
|
|
6480
|
+
this.scrollmap = this.scrollmapOverride;
|
|
6481
|
+
this.clickmap = this.clickmapOverride;
|
|
5527
6482
|
}
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
6483
|
+
htmlRender = async (props) => {
|
|
6484
|
+
const { decoded, target, portalCanvasId, useproxy, logerror } = props;
|
|
6485
|
+
if (!decoded || decoded.length === 0 || !target)
|
|
6486
|
+
return this;
|
|
6487
|
+
try {
|
|
6488
|
+
const merged = this.mergeForHtml(decoded);
|
|
6489
|
+
await this.setup(target, { version: decoded[0].envelope.version, dom: merged.dom, useproxy, portalCanvasId });
|
|
6490
|
+
await this.renderLoop(this, { events: merged.events, target, useproxy });
|
|
6491
|
+
}
|
|
6492
|
+
catch (e) {
|
|
6493
|
+
if (logerror)
|
|
6494
|
+
logerror(e);
|
|
6495
|
+
}
|
|
5536
6496
|
return this;
|
|
5537
6497
|
};
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
6498
|
+
htmlCached = async (cacheKey, options) => {
|
|
6499
|
+
const { decoded, target } = options;
|
|
6500
|
+
if (!decoded || decoded.length === 0 || !target)
|
|
6501
|
+
return this;
|
|
6502
|
+
const fullKey = buildCacheKey(cacheKey, options.shortCircuitStrategy);
|
|
6503
|
+
const cached = await htmlCache.get(fullKey);
|
|
6504
|
+
if (cached) {
|
|
6505
|
+
try {
|
|
6506
|
+
await this.buildHtmlByCached(cached, options);
|
|
6507
|
+
return this;
|
|
6508
|
+
}
|
|
6509
|
+
catch (e) {
|
|
6510
|
+
options?.logerror?.(e);
|
|
6511
|
+
}
|
|
6512
|
+
}
|
|
6513
|
+
await this.buildHtmlForCache(fullKey, options);
|
|
6514
|
+
return this;
|
|
5541
6515
|
};
|
|
5542
6516
|
/**
|
|
5543
6517
|
* Render attention/engagement map.
|
|
@@ -5549,75 +6523,249 @@ class GXVisualizer extends Visualizer {
|
|
|
5549
6523
|
this.clearmapOverride();
|
|
5550
6524
|
this.attentionMap.attention(attentionData, avgFold, this, isSecondary);
|
|
5551
6525
|
};
|
|
6526
|
+
/**
|
|
6527
|
+
* Render discrete color bands for scrollRevenue / scrollAttention data.
|
|
6528
|
+
* @param data - Array of bucket inputs with position markers (0, 5, 10, ..., 95) and percent values
|
|
6529
|
+
*/
|
|
6530
|
+
scrollBucket = async (data) => {
|
|
6531
|
+
this.clearmapOverride();
|
|
6532
|
+
await this.scrollBucketMap.renderBucket(data);
|
|
6533
|
+
};
|
|
6534
|
+
scrollmapOverride = (data, averageFold, addMarkers) => {
|
|
6535
|
+
this.clearmapOverride();
|
|
6536
|
+
this.originalScrollmap(data, averageFold, addMarkers);
|
|
6537
|
+
};
|
|
6538
|
+
clickmapOverride = (activity) => {
|
|
6539
|
+
this.clearmapOverride();
|
|
6540
|
+
this.originalClickmap(activity);
|
|
6541
|
+
};
|
|
6542
|
+
buildHtmlByCached = async (cached, options) => {
|
|
6543
|
+
const { target, useproxy, portalCanvasId } = options;
|
|
6544
|
+
if (!cached || !target)
|
|
6545
|
+
throw new Error('Failed to render HTML cached');
|
|
6546
|
+
const doc = target.document;
|
|
6547
|
+
try {
|
|
6548
|
+
await this.setup(target, { version: cached.version, useproxy, portalCanvasId });
|
|
6549
|
+
doc.open();
|
|
6550
|
+
doc.write(cached.html);
|
|
6551
|
+
doc.close();
|
|
6552
|
+
const process = async () => {
|
|
6553
|
+
this.layout.hydrate(doc);
|
|
6554
|
+
// Replay shadow DOM from initial Discover event
|
|
6555
|
+
if (cached.specialDom) {
|
|
6556
|
+
this.layout.markup(cached.specialDom, useproxy);
|
|
6557
|
+
}
|
|
6558
|
+
// Replay shadow DOM mutations + StyleSheet + CustomElement
|
|
6559
|
+
await this.renderLoop(this, { ...options, events: cached.specialEvents });
|
|
6560
|
+
};
|
|
6561
|
+
await retry(() => doc.readyState === 'complete', { timeout: 30000, label: 'DOM ready' });
|
|
6562
|
+
await process();
|
|
6563
|
+
return this;
|
|
6564
|
+
}
|
|
6565
|
+
catch (e) {
|
|
6566
|
+
throw new Error('Failed to render HTML cached', { cause: e });
|
|
6567
|
+
}
|
|
6568
|
+
};
|
|
6569
|
+
buildHtmlForCache = async (cacheKey, options) => {
|
|
6570
|
+
const { decoded, target, useproxy, portalCanvasId, logerror } = options;
|
|
6571
|
+
if (!decoded || decoded.length === 0 || !target)
|
|
6572
|
+
return this;
|
|
6573
|
+
try {
|
|
6574
|
+
const merged = this.mergeForHtml(decoded);
|
|
6575
|
+
await this.setup(target, { version: decoded[0].envelope.version, dom: merged.dom, useproxy, portalCanvasId });
|
|
6576
|
+
await this.renderLoop(this, { events: merged.events, target, useproxy });
|
|
6577
|
+
const timestamp = Date.now();
|
|
6578
|
+
const version = decoded[0].envelope.version;
|
|
6579
|
+
const html = target.document.documentElement.outerHTML;
|
|
6580
|
+
const shadowHostTags = collectShadowHostTags(target.document);
|
|
6581
|
+
const specialDom = extractSpecialDom(merged.dom, merged.events, shadowHostTags);
|
|
6582
|
+
const specialEvents = extractSpecialEvents(merged.events, merged.dom, shadowHostTags);
|
|
6583
|
+
void htmlCache.set({ key: cacheKey, html, specialDom, specialEvents, version, timestamp });
|
|
6584
|
+
}
|
|
6585
|
+
catch (e) {
|
|
6586
|
+
if (logerror)
|
|
6587
|
+
logerror(e);
|
|
6588
|
+
}
|
|
6589
|
+
return this;
|
|
6590
|
+
};
|
|
6591
|
+
renderLoop = async (ctx, options) => {
|
|
6592
|
+
await renderLoop(ctx, options);
|
|
6593
|
+
};
|
|
6594
|
+
setupOverride = async (target, options) => {
|
|
6595
|
+
// Mirror original Visualizer.setup(): only reset (remove listeners/state), not clear canvas.
|
|
6596
|
+
// The canvas lives in the old window's DOM which will be replaced by originalSetup's reset().
|
|
6597
|
+
this.attentionMap?.reset();
|
|
6598
|
+
this.scrollBucketMap?.reset();
|
|
6599
|
+
await this.originalSetup(target, options);
|
|
6600
|
+
this.attentionMap = new AttentionMapRenderer(this.state);
|
|
6601
|
+
this.scrollBucketMap = new ScrollBucketRenderer(this.heatmap);
|
|
6602
|
+
return this;
|
|
6603
|
+
};
|
|
6604
|
+
clearmapOverride = () => {
|
|
6605
|
+
this.originalClearmap();
|
|
6606
|
+
this.attentionMap?.clear();
|
|
6607
|
+
this.scrollBucketMap?.clear();
|
|
6608
|
+
};
|
|
5552
6609
|
}
|
|
5553
6610
|
|
|
5554
|
-
const
|
|
5555
|
-
|
|
5556
|
-
const
|
|
5557
|
-
const vizRef = useHeatmapVizRectContext((s) => s.vizRef);
|
|
5558
|
-
const setVizRef = useHeatmapVizRectContext((s) => s.setVizRef);
|
|
5559
|
-
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
5560
|
-
const setIsRenderViz = useHeatmapVizContext((s) => s.setIsRenderViz);
|
|
5561
|
-
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
5562
|
-
const contentWidth = useHeatmapWidthByDevice();
|
|
6611
|
+
const perf$1 = createPerfTimer('Render');
|
|
6612
|
+
const useHeatmapIframeProcessor = () => {
|
|
6613
|
+
const shopId = useHeatmapConfigStore((s) => s.shopId);
|
|
5563
6614
|
const deviceType = useHeatmapSettingContext((s) => s.deviceType);
|
|
5564
|
-
const
|
|
5565
|
-
const
|
|
5566
|
-
|
|
6615
|
+
const viewport = useHeatmapViewportByDevice();
|
|
6616
|
+
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
6617
|
+
const setIsDomLoaded = useHeatmapVizContext((s) => s.setIsDomLoaded);
|
|
6618
|
+
const helperRef = useRef(null);
|
|
6619
|
+
const pendingRef = useRef(null);
|
|
6620
|
+
const reset = useCallback(() => {
|
|
6621
|
+
pendingRef.current = null;
|
|
6622
|
+
}, []);
|
|
6623
|
+
const run = useCallback((iframe, t0, abort) => {
|
|
6624
|
+
if (viewport.width === 0 || viewport.height === 0) {
|
|
6625
|
+
pendingRef.current = { iframe, t0, abort };
|
|
5567
6626
|
return;
|
|
5568
|
-
|
|
6627
|
+
}
|
|
6628
|
+
startIframe({
|
|
6629
|
+
helperRef,
|
|
6630
|
+
iframe,
|
|
6631
|
+
shopId,
|
|
6632
|
+
deviceType,
|
|
6633
|
+
size: viewport,
|
|
6634
|
+
t0,
|
|
6635
|
+
onSuccess: (height) => {
|
|
6636
|
+
if (abort.signal.aborted)
|
|
6637
|
+
return;
|
|
6638
|
+
if (height)
|
|
6639
|
+
setIframeHeight(height);
|
|
6640
|
+
setIsDomLoaded(true);
|
|
6641
|
+
},
|
|
6642
|
+
});
|
|
6643
|
+
}, [deviceType]);
|
|
6644
|
+
// Retry when dims become available
|
|
6645
|
+
useEffect(() => {
|
|
6646
|
+
if (viewport.width === 0 || viewport.height === 0)
|
|
5569
6647
|
return;
|
|
5570
|
-
|
|
5571
|
-
if (!vizRef)
|
|
5572
|
-
setVizRef(visualizer);
|
|
5573
|
-
setIsRenderViz(false);
|
|
5574
|
-
const iframe = iframeRef.current;
|
|
5575
|
-
if (!iframe?.contentWindow)
|
|
6648
|
+
if (!pendingRef.current)
|
|
5576
6649
|
return;
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
6650
|
+
const { iframe, t0, abort } = pendingRef.current;
|
|
6651
|
+
pendingRef.current = null;
|
|
6652
|
+
if (abort.signal.aborted)
|
|
6653
|
+
return;
|
|
6654
|
+
startIframe({
|
|
6655
|
+
helperRef,
|
|
6656
|
+
iframe,
|
|
6657
|
+
shopId,
|
|
6658
|
+
deviceType,
|
|
6659
|
+
size: viewport,
|
|
6660
|
+
t0,
|
|
6661
|
+
onSuccess: (height) => {
|
|
6662
|
+
if (abort.signal.aborted)
|
|
6663
|
+
return;
|
|
6664
|
+
if (height)
|
|
6665
|
+
setIframeHeight(height);
|
|
6666
|
+
setIsDomLoaded(true);
|
|
6667
|
+
},
|
|
5582
6668
|
});
|
|
5583
|
-
|
|
5584
|
-
}, [wrapperHeight, contentWidth, deviceType]);
|
|
6669
|
+
}, [viewport]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
5585
6670
|
useEffect(() => {
|
|
5586
|
-
if (!data || data.length === 0)
|
|
5587
|
-
return;
|
|
5588
|
-
const decoded = decodeArrayClarity(data);
|
|
5589
|
-
renderHeatmap(decoded);
|
|
5590
6671
|
return () => {
|
|
5591
|
-
|
|
6672
|
+
helperRef.current?.stop();
|
|
6673
|
+
helperRef.current = null;
|
|
5592
6674
|
};
|
|
5593
|
-
}, [
|
|
5594
|
-
return {
|
|
5595
|
-
iframeRef,
|
|
5596
|
-
};
|
|
6675
|
+
}, []);
|
|
6676
|
+
return { run, reset };
|
|
5597
6677
|
};
|
|
5598
|
-
|
|
6678
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
6679
|
+
function startIframe({ helperRef, iframe, shopId, deviceType = EDeviceType.Desktop, size, t0, onSuccess, }) {
|
|
5599
6680
|
const docWidth = size.width ?? 0;
|
|
5600
6681
|
const docHeight = size.height ?? 0;
|
|
5601
6682
|
if (docHeight === 0)
|
|
5602
6683
|
return;
|
|
5603
|
-
stop();
|
|
5604
|
-
start
|
|
5605
|
-
|
|
6684
|
+
helperRef.current?.stop();
|
|
6685
|
+
const tHelper = perf$1.mark('IframeHelper.start');
|
|
6686
|
+
const helper = createIframeHelper();
|
|
6687
|
+
helperRef.current = helper;
|
|
6688
|
+
helper.start({
|
|
6689
|
+
deviceType,
|
|
5606
6690
|
targetWidth: docWidth,
|
|
5607
6691
|
targetHeight: docHeight,
|
|
5608
|
-
iframe
|
|
6692
|
+
iframe,
|
|
5609
6693
|
debug: true,
|
|
6694
|
+
shopId,
|
|
5610
6695
|
onSuccess: (data) => {
|
|
6696
|
+
perf$1.measure('IframeHelper processing', tHelper);
|
|
6697
|
+
perf$1.measure('Total render', t0);
|
|
5611
6698
|
iframe.style.height = `${data.height}px`;
|
|
5612
6699
|
onSuccess(data.height);
|
|
5613
6700
|
},
|
|
5614
6701
|
});
|
|
5615
|
-
// fixer.recalculate();
|
|
5616
6702
|
}
|
|
5617
6703
|
|
|
6704
|
+
const perf = createPerfTimer('Render');
|
|
6705
|
+
const useHeatmapRenderDom = () => {
|
|
6706
|
+
const viewId = useViewIdContext();
|
|
6707
|
+
const data = useHeatmapDataContext((s) => s.data);
|
|
6708
|
+
const excludeClassNames = useHeatmapConfigStore((s) => s.excludeClassNames);
|
|
6709
|
+
const setVizRef = useHeatmapVizRectContext((s) => s.setVizRef);
|
|
6710
|
+
const vizRef = useHeatmapVizRectContext((s) => s.vizRef);
|
|
6711
|
+
const setIsDomLoaded = useHeatmapVizContext((s) => s.setIsDomLoaded);
|
|
6712
|
+
const deviceType = useHeatmapSettingContext((s) => s.deviceType);
|
|
6713
|
+
const heatmapType = useHeatmapSettingContext((s) => s.heatmapType);
|
|
6714
|
+
const elementToShow = useHeatmapDataContext((s) => s.dataInfo?.elementToShow);
|
|
6715
|
+
const dataHash = useHeatmapDataContext((s) => s.dataHash);
|
|
6716
|
+
const iframeRef = useRef(null);
|
|
6717
|
+
const abortRef = useRef(null);
|
|
6718
|
+
const elementToShowRef = useRef(null);
|
|
6719
|
+
const dataHashRef = useRef(null);
|
|
6720
|
+
const heatmapTypeRef = useRef(heatmapType);
|
|
6721
|
+
elementToShowRef.current = elementToShow ?? null;
|
|
6722
|
+
dataHashRef.current = dataHash ?? null;
|
|
6723
|
+
heatmapTypeRef.current = heatmapType;
|
|
6724
|
+
const { run: runIframeSetup, reset: resetIframeSetup } = useHeatmapIframeProcessor();
|
|
6725
|
+
const renderHeatmap = useCallback(async (payloads) => {
|
|
6726
|
+
if (!payloads || payloads.length === 0)
|
|
6727
|
+
return;
|
|
6728
|
+
const iframe = iframeRef.current;
|
|
6729
|
+
const contentWindow = iframe?.contentWindow;
|
|
6730
|
+
if (!contentWindow)
|
|
6731
|
+
return;
|
|
6732
|
+
abortRef.current?.abort();
|
|
6733
|
+
const abort = new AbortController();
|
|
6734
|
+
abortRef.current = abort;
|
|
6735
|
+
resetIframeSetup();
|
|
6736
|
+
const t0 = perf.mark('RenderHeatmap start');
|
|
6737
|
+
const visualizer = vizRef ?? new GXVisualizer();
|
|
6738
|
+
if (!vizRef)
|
|
6739
|
+
setVizRef(visualizer);
|
|
6740
|
+
visualizer.configure({ excludeClassNames });
|
|
6741
|
+
setIsDomLoaded(false);
|
|
6742
|
+
// Phase 1: render DOM — does not depend on contentWidth/wrapperHeight
|
|
6743
|
+
const cacheKey = dataHashRef.current;
|
|
6744
|
+
const options = {
|
|
6745
|
+
decoded: payloads,
|
|
6746
|
+
target: contentWindow,
|
|
6747
|
+
portalCanvasId: viewId,
|
|
6748
|
+
logerror: (error) => {
|
|
6749
|
+
console.error('Error rendering HTML', error);
|
|
6750
|
+
},
|
|
6751
|
+
};
|
|
6752
|
+
await perf.wrap('RenderHtml', () => cacheKey ? visualizer.htmlCached(cacheKey, options) : visualizer.htmlRender(options));
|
|
6753
|
+
if (abort.signal.aborted)
|
|
6754
|
+
return;
|
|
6755
|
+
// Phase 2: iframe setup — deferred to useIframeSetup (handles dims dependency)
|
|
6756
|
+
runIframeSetup(iframe, t0, abort);
|
|
6757
|
+
}, [deviceType]);
|
|
6758
|
+
useEffect(() => {
|
|
6759
|
+
if (!data || data.length === 0)
|
|
6760
|
+
return;
|
|
6761
|
+
renderHeatmap(decodeArrayClarity(data));
|
|
6762
|
+
}, [data, renderHeatmap]);
|
|
6763
|
+
return { iframeRef };
|
|
6764
|
+
};
|
|
6765
|
+
|
|
5618
6766
|
const useReplayRender = () => {
|
|
5619
6767
|
const data = useHeatmapDataContext((s) => s.data);
|
|
5620
|
-
const
|
|
6768
|
+
const setIsDomLoaded = useHeatmapVizContext((s) => s.setIsDomLoaded);
|
|
5621
6769
|
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
5622
6770
|
const visualizerRef = useRef(null);
|
|
5623
6771
|
const iframeRef = useRef(null);
|
|
@@ -5637,7 +6785,7 @@ const useReplayRender = () => {
|
|
|
5637
6785
|
version: envelope.version,
|
|
5638
6786
|
onresize: (height) => {
|
|
5639
6787
|
height && setIframeHeight(height);
|
|
5640
|
-
|
|
6788
|
+
setIsDomLoaded(true);
|
|
5641
6789
|
},
|
|
5642
6790
|
mobile,
|
|
5643
6791
|
vNext: true,
|
|
@@ -5740,10 +6888,12 @@ const useReplayRender = () => {
|
|
|
5740
6888
|
const useHeatmapRenderByMode = (mode) => {
|
|
5741
6889
|
const heatmapResult = useMemo(() => {
|
|
5742
6890
|
switch (mode) {
|
|
5743
|
-
case
|
|
5744
|
-
return
|
|
5745
|
-
case
|
|
6891
|
+
case EHeatmapMode.Heatmap:
|
|
6892
|
+
return useHeatmapRenderDom;
|
|
6893
|
+
case EHeatmapMode.Replay:
|
|
5746
6894
|
return useReplayRender;
|
|
6895
|
+
default:
|
|
6896
|
+
return useHeatmapRenderDom;
|
|
5747
6897
|
}
|
|
5748
6898
|
}, [mode]);
|
|
5749
6899
|
return heatmapResult();
|
|
@@ -5778,148 +6928,6 @@ const useContainerDimensions = (props) => {
|
|
|
5778
6928
|
return { containerWidth, containerHeight };
|
|
5779
6929
|
};
|
|
5780
6930
|
|
|
5781
|
-
const useContentDimensions = ({ iframeRef }) => {
|
|
5782
|
-
const contentWidth = useHeatmapWidthByDevice();
|
|
5783
|
-
useEffect(() => {
|
|
5784
|
-
if (!contentWidth)
|
|
5785
|
-
return;
|
|
5786
|
-
if (!iframeRef.current)
|
|
5787
|
-
return;
|
|
5788
|
-
// iframeRef.current.width = `${contentWidth}px`;
|
|
5789
|
-
}, [contentWidth, iframeRef]);
|
|
5790
|
-
return { contentWidth };
|
|
5791
|
-
};
|
|
5792
|
-
|
|
5793
|
-
const useObserveIframeHeight = (props) => {
|
|
5794
|
-
const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
5795
|
-
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
5796
|
-
const isRenderViz = useHeatmapVizContext((s) => s.isRenderViz);
|
|
5797
|
-
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
5798
|
-
const { iframeRef } = props;
|
|
5799
|
-
const resizeObserverRef = useRef(null);
|
|
5800
|
-
const mutationObserverRef = useRef(null);
|
|
5801
|
-
const debounceTimerRef = useRef(null);
|
|
5802
|
-
const lastHeightRef = useRef(0);
|
|
5803
|
-
const animationFrameRef = useRef(null);
|
|
5804
|
-
const updateIframeHeight = useCallback(() => {
|
|
5805
|
-
const iframe = iframeRef.current;
|
|
5806
|
-
if (!iframe)
|
|
5807
|
-
return;
|
|
5808
|
-
try {
|
|
5809
|
-
const iframeDocument = iframe.contentDocument;
|
|
5810
|
-
const iframeBody = iframeDocument?.body;
|
|
5811
|
-
const iframeDocumentElement = iframeDocument?.documentElement;
|
|
5812
|
-
if (!iframeBody || !iframeDocumentElement)
|
|
5813
|
-
return;
|
|
5814
|
-
// iframe.style.height = 'auto'; // TODO: check if this is needed
|
|
5815
|
-
requestAnimationFrame(() => {
|
|
5816
|
-
iframe.style.height = `${wrapperHeight}px`;
|
|
5817
|
-
const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
|
|
5818
|
-
const documentHeight = Math.max(iframeDocumentElement.scrollHeight, iframeDocumentElement.offsetHeight, iframeDocumentElement.clientHeight);
|
|
5819
|
-
const actualHeight = Math.max(bodyHeight, documentHeight);
|
|
5820
|
-
if (actualHeight > 0) {
|
|
5821
|
-
lastHeightRef.current = actualHeight;
|
|
5822
|
-
// iframe.height = `${actualHeight}px`;
|
|
5823
|
-
iframe.style.height = `${actualHeight}px`;
|
|
5824
|
-
setIframeHeight(actualHeight);
|
|
5825
|
-
}
|
|
5826
|
-
});
|
|
5827
|
-
}
|
|
5828
|
-
catch (error) {
|
|
5829
|
-
console.warn('Cannot measure iframe content:', error);
|
|
5830
|
-
}
|
|
5831
|
-
}, [iframeRef, wrapperHeight]);
|
|
5832
|
-
useCallback(() => {
|
|
5833
|
-
// Cancel pending updates
|
|
5834
|
-
if (debounceTimerRef.current) {
|
|
5835
|
-
clearTimeout(debounceTimerRef.current);
|
|
5836
|
-
}
|
|
5837
|
-
if (animationFrameRef.current) {
|
|
5838
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
5839
|
-
}
|
|
5840
|
-
debounceTimerRef.current = setTimeout(() => {
|
|
5841
|
-
animationFrameRef.current = requestAnimationFrame(() => {
|
|
5842
|
-
updateIframeHeight();
|
|
5843
|
-
});
|
|
5844
|
-
}, 50);
|
|
5845
|
-
}, [updateIframeHeight]);
|
|
5846
|
-
// Immediate update không debounce (cho ResizeObserver)
|
|
5847
|
-
const immediateUpdate = useCallback(() => {
|
|
5848
|
-
if (animationFrameRef.current) {
|
|
5849
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
5850
|
-
}
|
|
5851
|
-
animationFrameRef.current = requestAnimationFrame(() => {
|
|
5852
|
-
updateIframeHeight();
|
|
5853
|
-
});
|
|
5854
|
-
}, [updateIframeHeight]);
|
|
5855
|
-
useEffect(() => {
|
|
5856
|
-
const iframe = iframeRef.current;
|
|
5857
|
-
if (!iframe || !iframeHeight || !isRenderViz)
|
|
5858
|
-
return;
|
|
5859
|
-
const setupObservers = () => {
|
|
5860
|
-
try {
|
|
5861
|
-
const iframeDocument = iframe.contentDocument;
|
|
5862
|
-
const iframeBody = iframeDocument?.body;
|
|
5863
|
-
if (!iframeBody)
|
|
5864
|
-
return;
|
|
5865
|
-
// Cleanup existing observers
|
|
5866
|
-
if (resizeObserverRef.current) {
|
|
5867
|
-
resizeObserverRef.current.disconnect();
|
|
5868
|
-
}
|
|
5869
|
-
if (mutationObserverRef.current) {
|
|
5870
|
-
mutationObserverRef.current.disconnect();
|
|
5871
|
-
}
|
|
5872
|
-
if (typeof window.ResizeObserver !== 'undefined') {
|
|
5873
|
-
resizeObserverRef.current = new ResizeObserver(immediateUpdate);
|
|
5874
|
-
resizeObserverRef.current.observe(iframeBody);
|
|
5875
|
-
const iframeDocumentElement = iframeDocument?.documentElement;
|
|
5876
|
-
if (iframeDocumentElement) {
|
|
5877
|
-
resizeObserverRef.current.observe(iframeDocumentElement);
|
|
5878
|
-
}
|
|
5879
|
-
}
|
|
5880
|
-
if (typeof window.MutationObserver !== 'undefined') {
|
|
5881
|
-
mutationObserverRef.current = new MutationObserver(immediateUpdate);
|
|
5882
|
-
mutationObserverRef.current.observe(iframeBody, {
|
|
5883
|
-
childList: true,
|
|
5884
|
-
subtree: true,
|
|
5885
|
-
attributes: true,
|
|
5886
|
-
attributeFilter: ['style', 'class'],
|
|
5887
|
-
characterData: false,
|
|
5888
|
-
});
|
|
5889
|
-
}
|
|
5890
|
-
updateIframeHeight();
|
|
5891
|
-
}
|
|
5892
|
-
catch (error) {
|
|
5893
|
-
console.warn('Cannot access iframe content:', error);
|
|
5894
|
-
}
|
|
5895
|
-
};
|
|
5896
|
-
if (iframe.contentDocument?.readyState === 'complete') {
|
|
5897
|
-
setupObservers();
|
|
5898
|
-
}
|
|
5899
|
-
else {
|
|
5900
|
-
iframe.addEventListener('load', setupObservers, { once: true });
|
|
5901
|
-
}
|
|
5902
|
-
return () => {
|
|
5903
|
-
// Cleanup observers
|
|
5904
|
-
if (resizeObserverRef.current) {
|
|
5905
|
-
resizeObserverRef.current.disconnect();
|
|
5906
|
-
}
|
|
5907
|
-
if (mutationObserverRef.current) {
|
|
5908
|
-
mutationObserverRef.current.disconnect();
|
|
5909
|
-
}
|
|
5910
|
-
// Cleanup timers
|
|
5911
|
-
if (debounceTimerRef.current) {
|
|
5912
|
-
clearTimeout(debounceTimerRef.current);
|
|
5913
|
-
}
|
|
5914
|
-
if (animationFrameRef.current) {
|
|
5915
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
5916
|
-
}
|
|
5917
|
-
iframe.removeEventListener('load', setupObservers);
|
|
5918
|
-
};
|
|
5919
|
-
}, [iframeRef, iframeHeight, isRenderViz]);
|
|
5920
|
-
return {};
|
|
5921
|
-
};
|
|
5922
|
-
|
|
5923
6931
|
const useScaleCalculation = (props) => {
|
|
5924
6932
|
const widthScale = useHeatmapVizContext((s) => s.widthScale);
|
|
5925
6933
|
const zoomRatio = useHeatmapVizContext((s) => s.zoomRatio);
|
|
@@ -5929,9 +6937,10 @@ const useScaleCalculation = (props) => {
|
|
|
5929
6937
|
const setScale = useHeatmapVizContext((s) => s.setScale);
|
|
5930
6938
|
const setIsScaledToFit = useHeatmapVizContext((s) => s.setIsScaledToFit);
|
|
5931
6939
|
const setMinZoomRatio = useHeatmapVizContext((s) => s.setMinZoomRatio);
|
|
5932
|
-
const
|
|
6940
|
+
const viewport = useHeatmapViewportByDevice();
|
|
6941
|
+
const { containerWidth, containerHeight, iframeHeight } = props;
|
|
5933
6942
|
const calculateScaleResult = useCallback(() => {
|
|
5934
|
-
if (containerWidth > 0 &&
|
|
6943
|
+
if (containerWidth > 0 && viewport.width > 0 && containerHeight > 0 && iframeHeight > 0) {
|
|
5935
6944
|
// 1. Calculate available dimensions
|
|
5936
6945
|
const availableWidth = containerWidth - HEATMAP_CONFIG['padding'] * 2;
|
|
5937
6946
|
const toolbarHeight = HEATMAP_CONFIG['heightToolbar'] || 0;
|
|
@@ -5939,12 +6948,12 @@ const useScaleCalculation = (props) => {
|
|
|
5939
6948
|
const availableHeight = containerHeight - toolbarHeight - paddingTotal;
|
|
5940
6949
|
// 2. Calculate widthScale (base scale to fit content width into container width)
|
|
5941
6950
|
// This represents 100% zoom (fit to width)
|
|
5942
|
-
const widthScale = Math.min(availableWidth /
|
|
6951
|
+
const widthScale = Math.min(availableWidth / viewport.width, 1);
|
|
5943
6952
|
// 3. Calculate minZoomRatio (zoom ratio to fit height)
|
|
5944
6953
|
// At minZoomRatio, the content should fit entirely within the container height
|
|
5945
|
-
// Formula:
|
|
5946
|
-
// => minZoomRatio = (availableHeight / (
|
|
5947
|
-
const calculatedMinZoomRatio = (availableHeight / (
|
|
6954
|
+
// Formula: iframeHeight * widthScale * (minZoomRatio / 100) = availableHeight
|
|
6955
|
+
// => minZoomRatio = (availableHeight / (iframeHeight * widthScale)) * 100
|
|
6956
|
+
const calculatedMinZoomRatio = (availableHeight / (iframeHeight * widthScale)) * 100;
|
|
5948
6957
|
// Limit minZoomRatio: cannot exceed MAX_ZOOM_RATIO (100%)
|
|
5949
6958
|
// and should have a reasonable minimum (e.g., 1%)
|
|
5950
6959
|
const finalMinZoomRatio = Math.max(1, Math.min(calculatedMinZoomRatio, maxZoomRatio));
|
|
@@ -5963,7 +6972,7 @@ const useScaleCalculation = (props) => {
|
|
|
5963
6972
|
setIsScaledToFit(isCurrentlyFitted);
|
|
5964
6973
|
setMinZoomRatio(finalMinZoomRatio);
|
|
5965
6974
|
}
|
|
5966
|
-
}, [containerWidth, containerHeight,
|
|
6975
|
+
}, [containerWidth, containerHeight, viewport.width, iframeHeight, zoomRatio, maxZoomRatio]);
|
|
5967
6976
|
useEffect(() => {
|
|
5968
6977
|
calculateScaleResult();
|
|
5969
6978
|
}, [calculateScaleResult]);
|
|
@@ -5996,20 +7005,15 @@ const useHeatmapScale = (props) => {
|
|
|
5996
7005
|
// 1. Observe container dimensions
|
|
5997
7006
|
const { containerWidth, containerHeight } = useContainerDimensions({ wrapperRef });
|
|
5998
7007
|
// 2. Get content dimensions from config
|
|
5999
|
-
const
|
|
7008
|
+
const viewport = useHeatmapViewportByDevice();
|
|
6000
7009
|
// 3. Observe iframe height (now reacts to width changes)
|
|
6001
|
-
useObserveIframeHeight({ iframeRef });
|
|
7010
|
+
// useObserveIframeHeight({ iframeRef });
|
|
6002
7011
|
// 4. Calculate scale
|
|
6003
|
-
const { widthScale } = useScaleCalculation({
|
|
6004
|
-
containerWidth,
|
|
6005
|
-
containerHeight,
|
|
6006
|
-
contentWidth,
|
|
6007
|
-
contentHeight: iframeHeight,
|
|
6008
|
-
});
|
|
7012
|
+
const { widthScale } = useScaleCalculation({ containerWidth, containerHeight, iframeHeight });
|
|
6009
7013
|
// 5. Setup scroll sync
|
|
6010
7014
|
const { handleScroll } = useScrollSync({ widthScale, iframeRef });
|
|
6011
7015
|
const scaledHeight = iframeHeight * widthScale;
|
|
6012
|
-
const scaledWidth =
|
|
7016
|
+
const scaledWidth = viewport.width * widthScale;
|
|
6013
7017
|
return {
|
|
6014
7018
|
scaledWidth,
|
|
6015
7019
|
scaledHeight,
|
|
@@ -6215,10 +7219,10 @@ const useScrollmapZones = (options) => {
|
|
|
6215
7219
|
const newZones = createZones(scrollmap);
|
|
6216
7220
|
setZones(newZones);
|
|
6217
7221
|
setIsReady(true);
|
|
6218
|
-
logger$
|
|
7222
|
+
logger$3.log(`[useScrollmap] Created ${newZones.length} zones in ${mode} mode`);
|
|
6219
7223
|
}
|
|
6220
7224
|
catch (error) {
|
|
6221
|
-
logger$
|
|
7225
|
+
logger$3.error('[useScrollmap] Error:', error);
|
|
6222
7226
|
setIsReady(false);
|
|
6223
7227
|
}
|
|
6224
7228
|
}, [enabled, scrollmap, mode, createZones]);
|
|
@@ -7048,11 +8052,11 @@ const AutoScrollHandler = ({ visualRef }) => {
|
|
|
7048
8052
|
};
|
|
7049
8053
|
|
|
7050
8054
|
const PortalAreaRenderer = ({ iframeRef, visualRef, shadowRoot, onAreaCreated, onAreaClick, }) => {
|
|
7051
|
-
const
|
|
8055
|
+
const isDomLoaded = useHeatmapVizContext((s) => s.isDomLoaded);
|
|
7052
8056
|
const iframeDocument = iframeRef.current?.contentDocument || undefined;
|
|
7053
8057
|
const { shadowContainer, isReady } = useAreaRendererContainer(iframeDocument, shadowRoot);
|
|
7054
|
-
useAreaRectSync({ iframeDocument, shadowRoot, enabled: isReady &&
|
|
7055
|
-
useAreaPositionsUpdater({ iframeRef, visualRef, enabled: isReady &&
|
|
8058
|
+
useAreaRectSync({ iframeDocument, shadowRoot, enabled: isReady && isDomLoaded });
|
|
8059
|
+
useAreaPositionsUpdater({ iframeRef, visualRef, enabled: isReady && isDomLoaded });
|
|
7056
8060
|
if (!shadowContainer || !isReady)
|
|
7057
8061
|
return null;
|
|
7058
8062
|
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 +8065,7 @@ const PortalAreaRenderer = ({ iframeRef, visualRef, shadowRoot, onAreaCreated, o
|
|
|
7061
8065
|
const VizAreaClick = ({ iframeRef, visualRef, shadowRoot, autoCreateTopN = 10, enableOverlapResolution = true, onAreaClick, }) => {
|
|
7062
8066
|
const clickAreas = useHeatmapDataContext((s) => s.clickAreas);
|
|
7063
8067
|
const resetView = useHeatmapAreaClickContext((s) => s.resetView);
|
|
7064
|
-
const
|
|
8068
|
+
const isDomLoaded = useHeatmapVizContext((s) => s.isDomLoaded);
|
|
7065
8069
|
useAreaTopAutoDetect({ autoCreateTopN, shadowRoot, disabled: !!clickAreas?.length });
|
|
7066
8070
|
useAreaFilterVisible({ iframeRef, enableOverlapResolution });
|
|
7067
8071
|
useAreaHydration({ shadowRoot });
|
|
@@ -7070,7 +8074,7 @@ const VizAreaClick = ({ iframeRef, visualRef, shadowRoot, autoCreateTopN = 10, e
|
|
|
7070
8074
|
resetView();
|
|
7071
8075
|
};
|
|
7072
8076
|
}, []);
|
|
7073
|
-
if (!iframeRef.current || !
|
|
8077
|
+
if (!iframeRef.current || !isDomLoaded)
|
|
7074
8078
|
return null;
|
|
7075
8079
|
return (jsx(Fragment, { children: jsx(PortalAreaRenderer, { iframeRef: iframeRef, visualRef: visualRef, shadowRoot: shadowRoot, onAreaClick: onAreaClick }) }));
|
|
7076
8080
|
};
|
|
@@ -7201,7 +8205,7 @@ const useAnchorPosition = (calloutRef, props) => {
|
|
|
7201
8205
|
const ElementMissing = ({ show = true, visualRef }) => {
|
|
7202
8206
|
const widthScale = useHeatmapVizContext((s) => s.widthScale);
|
|
7203
8207
|
const missingElementRef = useRef(null);
|
|
7204
|
-
const
|
|
8208
|
+
const viewport = useHeatmapViewportByDevice();
|
|
7205
8209
|
const [scrollPosition, setScrollPosition] = useState({ scrollTop: 0, scrollLeft: 0 });
|
|
7206
8210
|
useEffect(() => {
|
|
7207
8211
|
const container = visualRef.current;
|
|
@@ -7231,7 +8235,7 @@ const ElementMissing = ({ show = true, visualRef }) => {
|
|
|
7231
8235
|
const containerHeight = containerRect?.height ?? 0;
|
|
7232
8236
|
const topPosition = scrollTop + (containerHeight + elementHeightCenter) / 2;
|
|
7233
8237
|
const topPositionScaled = topPosition / widthScale;
|
|
7234
|
-
const leftPosition =
|
|
8238
|
+
const leftPosition = viewport.width / 2;
|
|
7235
8239
|
return (jsxs(Fragment, { children: [jsx("div", { className: "missingElement-backdrop", style: {
|
|
7236
8240
|
position: 'absolute',
|
|
7237
8241
|
top: 0,
|
|
@@ -7356,7 +8360,7 @@ const ElementOverlayComponent = (props) => {
|
|
|
7356
8360
|
const { type, element, onClick, elementId, hideOutline } = props;
|
|
7357
8361
|
const widthScale = useHeatmapVizContext((s) => s.widthScale);
|
|
7358
8362
|
const viewportHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
7359
|
-
const
|
|
8363
|
+
const viewport = useHeatmapViewportByDevice();
|
|
7360
8364
|
const overlayStyle = useMemo(() => {
|
|
7361
8365
|
const isInvalid = !element || (element.width === 0 && element.height === 0);
|
|
7362
8366
|
if (isInvalid)
|
|
@@ -7373,7 +8377,7 @@ const ElementOverlayComponent = (props) => {
|
|
|
7373
8377
|
const isHovered = type === 'hovered';
|
|
7374
8378
|
const badgeWidthScale = isHovered ? 1 : widthScale;
|
|
7375
8379
|
const showCallout = !!element?.mousePosition && !isHovered;
|
|
7376
|
-
return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: `heatmapElement heatmapElement--${type} ${hideOutline ? 'heatmapElement--hide-outline' : ''}`, id: elementId, style: overlayStyle, children: showCallout && jsx(ElementCalloutOverlay, { ...props }) }), jsx(BackdropCanvas, { activeElement: overlayStyle, viewportWidth:
|
|
8380
|
+
return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: `heatmapElement heatmapElement--${type} ${hideOutline ? 'heatmapElement--hide-outline' : ''}`, id: elementId, style: overlayStyle, children: showCallout && jsx(ElementCalloutOverlay, { ...props }) }), jsx(BackdropCanvas, { activeElement: overlayStyle, viewportWidth: viewport.width, viewportHeight: viewportHeight, show: !isHovered }), jsx(RankBadge, { hash: element.hash, show: isHovered, index: element.rank, elementRect: element, widthScale: badgeWidthScale, clickOnElement: onClick })] }));
|
|
7377
8381
|
};
|
|
7378
8382
|
ElementOverlayComponent.displayName = 'ElementOverlay';
|
|
7379
8383
|
const ElementOverlay = memo(ElementOverlayComponent);
|
|
@@ -7438,7 +8442,7 @@ const HeatmapElements = (props) => {
|
|
|
7438
8442
|
};
|
|
7439
8443
|
|
|
7440
8444
|
const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
|
|
7441
|
-
const
|
|
8445
|
+
const viewport = useHeatmapViewportByDevice();
|
|
7442
8446
|
const dataInfo = useHeatmapDataContext((s) => s.dataInfo);
|
|
7443
8447
|
const vizRef = useHeatmapVizRectContext((s) => s.vizRef);
|
|
7444
8448
|
const visualizer = {
|
|
@@ -7452,7 +8456,7 @@ const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
|
|
|
7452
8456
|
if (!iframeRef.current)
|
|
7453
8457
|
return null;
|
|
7454
8458
|
return (jsx(HeatmapElements, { visualizer: visualizer, visualRef: visualRef, iframeRef: iframeRef, wrapperRef: wrapperRef, heatmapInfo: dataInfo, isVisible: true, positionMode: DEFAULT_POSITION_MODE, isHideTopRank: true, iframeDimensions: {
|
|
7455
|
-
width:
|
|
8459
|
+
width: viewport.width,
|
|
7456
8460
|
position: 'absolute',
|
|
7457
8461
|
top: 0,
|
|
7458
8462
|
left: 0,
|
|
@@ -7716,7 +8720,7 @@ const VizLoadingCanvas = () => {
|
|
|
7716
8720
|
const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHeight, onScroll, }) => {
|
|
7717
8721
|
const isLoadingCanvas = useHeatmapSettingContext((state) => state.isLoadingCanvas);
|
|
7718
8722
|
const widthScale = useHeatmapVizContext((s) => s.widthScale);
|
|
7719
|
-
const
|
|
8723
|
+
const viewport = useHeatmapViewportByDevice();
|
|
7720
8724
|
const contentHeight = calcContentHeight();
|
|
7721
8725
|
return (jsx("div", { ref: visualRef, className: "gx-hm-visual Polaris-Scrollable Polaris-Scrollable--vertical Polaris-Scrollable--scrollbarWidthThin Polaris-Scrollable--scrollbarGutterStable", onScroll: onScroll, style: {
|
|
7722
8726
|
overflowX: 'hidden',
|
|
@@ -7738,7 +8742,7 @@ const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHe
|
|
|
7738
8742
|
paddingBottom: HEATMAP_STYLE['viz']['paddingBottom'],
|
|
7739
8743
|
background: HEATMAP_STYLE['viz']['background'],
|
|
7740
8744
|
}, children: jsx("div", { className: "gx-hm-wrapper", ref: wrapperRef, style: {
|
|
7741
|
-
width:
|
|
8745
|
+
width: viewport.width,
|
|
7742
8746
|
height: iframeHeight,
|
|
7743
8747
|
transform: `scale(${widthScale})`,
|
|
7744
8748
|
transformOrigin: 'top center',
|
|
@@ -7751,14 +8755,14 @@ const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHe
|
|
|
7751
8755
|
}
|
|
7752
8756
|
};
|
|
7753
8757
|
|
|
7754
|
-
const VizDomRenderer = ({ mode
|
|
8758
|
+
const VizDomRenderer = ({ mode }) => {
|
|
7755
8759
|
const viewId = useViewIdContext();
|
|
7756
|
-
const
|
|
8760
|
+
const viewport = useHeatmapViewportByDevice();
|
|
7757
8761
|
const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
7758
8762
|
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
7759
8763
|
const wrapperRef = useRef(null);
|
|
7760
8764
|
const visualRef = useRef(null);
|
|
7761
|
-
const { iframeRef } =
|
|
8765
|
+
const { iframeRef } = useHeatmapRenderDom();
|
|
7762
8766
|
const { scaledHeight, handleScroll } = useHeatmapScale({ wrapperRef, iframeRef, visualRef });
|
|
7763
8767
|
useHeatmapCanvas();
|
|
7764
8768
|
useRenderCount('VizDomRenderer');
|
|
@@ -7766,7 +8770,7 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
|
|
|
7766
8770
|
const scrollTop = e.currentTarget.scrollTop;
|
|
7767
8771
|
handleScroll(scrollTop);
|
|
7768
8772
|
};
|
|
7769
|
-
return (jsxs(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, onScroll: onScroll, iframeHeight: iframeHeight, children: [jsx(VizClickmap, { iframeRef: iframeRef, visualRef: visualRef, wrapperRef: wrapperRef }), jsx("iframe", { ...HEATMAP_IFRAME, id: `clarity-iframe-${viewId}`, ref: iframeRef, width:
|
|
8773
|
+
return (jsxs(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, onScroll: onScroll, iframeHeight: iframeHeight, children: [jsx(VizClickmap, { iframeRef: iframeRef, visualRef: visualRef, wrapperRef: wrapperRef }), jsx("iframe", { ...HEATMAP_IFRAME, id: `clarity-iframe-${viewId}`, ref: iframeRef, width: viewport.width, height: wrapperHeight }), jsx(VizLoadingCanvas, {}), jsx(VizScrollMap, { visualRef: visualRef, iframeRef: iframeRef, wrapperRef: wrapperRef })] }));
|
|
7770
8774
|
};
|
|
7771
8775
|
|
|
7772
8776
|
const VizLoading = () => {
|
|
@@ -7783,8 +8787,9 @@ const VizLoading = () => {
|
|
|
7783
8787
|
const VizDomHeatmap = () => {
|
|
7784
8788
|
const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
7785
8789
|
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
8790
|
+
const setWrapperHeight = useHeatmapVizRectContext((s) => s.setWrapperHeight);
|
|
7786
8791
|
const setVizRef = useHeatmapVizRectContext((s) => s.setVizRef);
|
|
7787
|
-
const
|
|
8792
|
+
const setIsDomLoaded = useHeatmapVizContext((s) => s.setIsDomLoaded);
|
|
7788
8793
|
const setSelectedElement = useHeatmapClickContext((s) => s.setSelectedElement);
|
|
7789
8794
|
const setHoveredElement = useHeatmapHoverContext((s) => s.setHoveredElement);
|
|
7790
8795
|
// const setSelectedArea = useHeatmapAreaClickContext((s) => s.setSelectedArea);
|
|
@@ -7794,7 +8799,8 @@ const VizDomHeatmap = () => {
|
|
|
7794
8799
|
const cleanUp = () => {
|
|
7795
8800
|
setVizRef(null);
|
|
7796
8801
|
setIframeHeight(0);
|
|
7797
|
-
|
|
8802
|
+
setWrapperHeight(0);
|
|
8803
|
+
setIsDomLoaded(false);
|
|
7798
8804
|
setSelectedElement(null);
|
|
7799
8805
|
setHoveredElement(null);
|
|
7800
8806
|
// setSelectedArea(null);
|
|
@@ -7803,13 +8809,13 @@ const VizDomHeatmap = () => {
|
|
|
7803
8809
|
};
|
|
7804
8810
|
useEffect(() => {
|
|
7805
8811
|
return cleanUp;
|
|
7806
|
-
}, []);
|
|
7807
|
-
return (jsxs(VizContainer, { isActive: true, children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
|
|
8812
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
8813
|
+
return (jsxs(VizContainer, { isActive: true, children: [jsx(VizDomRenderer, { mode: EHeatmapMode.Heatmap }), iframeHeight === 0 && jsx(VizLoading, {})] }));
|
|
7808
8814
|
};
|
|
7809
8815
|
VizDomHeatmap.displayName = 'VizDomHeatmap';
|
|
7810
8816
|
|
|
7811
8817
|
const VizLiveRenderer = () => {
|
|
7812
|
-
const
|
|
8818
|
+
const viewport = useHeatmapViewportByDevice();
|
|
7813
8819
|
const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
7814
8820
|
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
7815
8821
|
const visualRef = useRef(null);
|
|
@@ -7820,13 +8826,13 @@ const VizLiveRenderer = () => {
|
|
|
7820
8826
|
const scrollTop = e.currentTarget.scrollTop;
|
|
7821
8827
|
handleScroll(scrollTop);
|
|
7822
8828
|
};
|
|
7823
|
-
return (jsx(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, iframeHeight: iframeHeight, onScroll: onScroll, children: jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width:
|
|
8829
|
+
return (jsx(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, iframeHeight: iframeHeight, onScroll: onScroll, children: jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: viewport.width, height: wrapperHeight, scrolling: "no" }) }));
|
|
7824
8830
|
};
|
|
7825
8831
|
|
|
7826
8832
|
const VizLiveHeatmap = () => {
|
|
7827
8833
|
const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
7828
8834
|
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
7829
|
-
|
|
8835
|
+
useHeatmapLiveContext((s) => s.reset);
|
|
7830
8836
|
const CompVizLoading = useHeatmapControlStore((state) => state.controls.VizLoading);
|
|
7831
8837
|
// TODO: Remove this after testing
|
|
7832
8838
|
useEffect(() => {
|
|
@@ -7877,11 +8883,11 @@ const ContentTopBar = () => {
|
|
|
7877
8883
|
}, children: CompTopBar && jsx(CompTopBar, {}) }));
|
|
7878
8884
|
};
|
|
7879
8885
|
|
|
7880
|
-
const HeatmapLayout = ({ data, clickmap, clickAreas, scrollmap, attentionMap, controls, dataInfo, isLoading, isLoadingCanvas, }) => {
|
|
8886
|
+
const HeatmapLayout = ({ shopId, data, clickmap, clickAreas, scrollmap, attentionMap, controls, dataInfo, isLoading, isLoadingCanvas, excludeClassNames, }) => {
|
|
7881
8887
|
useRegisterControl(controls);
|
|
7882
8888
|
useRegisterData(data, dataInfo);
|
|
7883
8889
|
useRegisterHeatmap({ clickmap, scrollmap, clickAreas, attentionMap });
|
|
7884
|
-
useRegisterConfig({ isLoading, isLoadingCanvas });
|
|
8890
|
+
useRegisterConfig({ isLoading, isLoadingCanvas, shopId, excludeClassNames });
|
|
7885
8891
|
// performanceLogger.configure({
|
|
7886
8892
|
// enabled: true,
|
|
7887
8893
|
// logToConsole: false,
|
|
@@ -7911,4 +8917,4 @@ const HeatmapLayout = ({ data, clickmap, clickAreas, scrollmap, attentionMap, co
|
|
|
7911
8917
|
}
|
|
7912
8918
|
};
|
|
7913
8919
|
|
|
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,
|
|
8920
|
+
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, useHeatmapViewportByDevice, useHeatmapVizContext, useHeatmapVizRectContext, useHoveredElement, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewIdContext, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };
|