@gemx-dev/heatmap-react 3.5.88 → 3.5.90

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.
Files changed (466) hide show
  1. package/dist/esm/components/Layout/HeatmapPreview.d.ts.map +1 -1
  2. package/dist/esm/helpers/index.d.ts +6 -6
  3. package/dist/esm/helpers/index.d.ts.map +1 -1
  4. package/dist/esm/helpers/viz-area-click/area-builder.d.ts.map +1 -1
  5. package/dist/esm/hooks/index.d.ts +0 -1
  6. package/dist/esm/hooks/index.d.ts.map +1 -1
  7. package/dist/esm/hooks/view-context/useHeatmapDataContext.d.ts +1 -0
  8. package/dist/esm/hooks/view-context/useHeatmapDataContext.d.ts.map +1 -1
  9. package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
  10. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  11. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts +0 -3
  12. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  13. package/dist/esm/index.js +1556 -2763
  14. package/dist/esm/index.mjs +1556 -2763
  15. package/dist/esm/{helpers/iframe-helper → libs/iframe-processor}/index.d.ts.map +1 -1
  16. package/dist/esm/libs/iframe-processor/lifecycle.d.ts +10 -0
  17. package/dist/esm/libs/iframe-processor/lifecycle.d.ts.map +1 -0
  18. package/dist/{umd/helpers/iframe-helper/iframe-fixer.d.ts → esm/libs/iframe-processor/orchestrator.d.ts} +1 -1
  19. package/dist/{umd/helpers/iframe-helper/iframe-fixer.d.ts.map → esm/libs/iframe-processor/orchestrator.d.ts.map} +1 -1
  20. package/dist/esm/libs/iframe-processor/processors/height-observer/index.d.ts +20 -0
  21. package/dist/esm/libs/iframe-processor/processors/height-observer/index.d.ts.map +1 -0
  22. package/dist/esm/libs/iframe-processor/processors/height-observer/observers.d.ts +7 -0
  23. package/dist/esm/libs/iframe-processor/processors/height-observer/observers.d.ts.map +1 -0
  24. package/dist/esm/libs/iframe-processor/processors/height-observer/types.d.ts +10 -0
  25. package/dist/esm/libs/iframe-processor/processors/height-observer/types.d.ts.map +1 -0
  26. package/dist/esm/libs/iframe-processor/processors/navigation/blockers.d.ts +9 -0
  27. package/dist/esm/libs/iframe-processor/processors/navigation/blockers.d.ts.map +1 -0
  28. package/dist/esm/libs/iframe-processor/processors/navigation/index.d.ts +20 -0
  29. package/dist/esm/libs/iframe-processor/processors/navigation/index.d.ts.map +1 -0
  30. package/dist/esm/libs/iframe-processor/processors/navigation/listeners.d.ts +12 -0
  31. package/dist/esm/libs/iframe-processor/processors/navigation/listeners.d.ts.map +1 -0
  32. package/dist/esm/libs/iframe-processor/processors/navigation/types.d.ts +7 -0
  33. package/dist/esm/libs/iframe-processor/processors/navigation/types.d.ts.map +1 -0
  34. package/dist/esm/libs/iframe-processor/processors/viewport/dimensions.d.ts +7 -0
  35. package/dist/esm/libs/iframe-processor/processors/viewport/dimensions.d.ts.map +1 -0
  36. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/gempages-swiper/fixes.d.ts +4 -0
  37. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/gempages-swiper/fixes.d.ts.map +1 -0
  38. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/gempages-swiper/index.d.ts +2 -0
  39. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/gempages-swiper/index.d.ts.map +1 -0
  40. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/gp-v7-slider/fixes.d.ts +3 -0
  41. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/gp-v7-slider/fixes.d.ts.map +1 -0
  42. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/gp-v7-slider/index.d.ts +2 -0
  43. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/gp-v7-slider/index.d.ts.map +1 -0
  44. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/viewport-unit-replacer/fixes.d.ts +7 -0
  45. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/viewport-unit-replacer/fixes.d.ts.map +1 -0
  46. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/viewport-unit-replacer/index.d.ts +2 -0
  47. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/fixes/viewport-unit-replacer/index.d.ts.map +1 -0
  48. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/index.d.ts +16 -0
  49. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/index.d.ts.map +1 -0
  50. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/registry.d.ts +14 -0
  51. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/registry.d.ts.map +1 -0
  52. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/types.d.ts +46 -0
  53. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/types.d.ts.map +1 -0
  54. package/dist/esm/{helpers/iframe-helper/iframe-viewport-replacer.d.ts → libs/iframe-processor/processors/viewport/index.d.ts} +2 -7
  55. package/dist/esm/libs/iframe-processor/processors/viewport/index.d.ts.map +1 -0
  56. package/dist/esm/libs/iframe-processor/processors/viewport/listeners.d.ts +3 -0
  57. package/dist/esm/libs/iframe-processor/processors/viewport/listeners.d.ts.map +1 -0
  58. package/dist/esm/libs/iframe-processor/processors/viewport/pipeline.d.ts +17 -0
  59. package/dist/esm/libs/iframe-processor/processors/viewport/pipeline.d.ts.map +1 -0
  60. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/index.d.ts +17 -0
  61. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/index.d.ts.map +1 -0
  62. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/registry.d.ts +16 -0
  63. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/registry.d.ts.map +1 -0
  64. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/shops/shop-566240210141053597/fixes.d.ts +11 -0
  65. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/shops/shop-566240210141053597/fixes.d.ts.map +1 -0
  66. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/shops/shop-566240210141053597/index.d.ts +2 -0
  67. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/shops/shop-566240210141053597/index.d.ts.map +1 -0
  68. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts +53 -0
  69. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts.map +1 -0
  70. package/dist/esm/{helpers/iframe-helper/iframe-style-enforcer.d.ts → libs/iframe-processor/processors/viewport/style-enforcer.d.ts} +2 -2
  71. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer.d.ts.map +1 -0
  72. package/dist/esm/libs/index.d.ts +1 -3
  73. package/dist/esm/libs/index.d.ts.map +1 -1
  74. package/dist/esm/libs/visualizer/AttentionMapRenderer.d.ts.map +1 -0
  75. package/dist/esm/libs/visualizer/ClickHeatmapRenderer.d.ts.map +1 -0
  76. package/dist/esm/libs/{GXVisualizer.d.ts → visualizer/GXVisualizer.d.ts} +0 -2
  77. package/dist/esm/libs/visualizer/GXVisualizer.d.ts.map +1 -0
  78. package/dist/esm/libs/visualizer/ScrollHeatmapRenderer.d.ts.map +1 -0
  79. package/dist/esm/libs/visualizer/index.d.ts +4 -0
  80. package/dist/esm/libs/visualizer/index.d.ts.map +1 -0
  81. package/dist/esm/libs/visualizer/types.d.ts.map +1 -0
  82. package/dist/esm/types/iframe-helper.d.ts +4 -0
  83. package/dist/esm/types/iframe-helper.d.ts.map +1 -1
  84. package/dist/esm/{helpers/dom-utils.d.ts → utils/dom.d.ts} +1 -1
  85. package/dist/esm/utils/dom.d.ts.map +1 -0
  86. package/dist/esm/utils/index.d.ts +9 -0
  87. package/dist/esm/utils/index.d.ts.map +1 -0
  88. package/dist/esm/utils/logger.d.ts.map +1 -0
  89. package/dist/esm/utils/observable.d.ts.map +1 -0
  90. package/dist/esm/utils/throttle.d.ts.map +1 -0
  91. package/dist/umd/components/Layout/HeatmapPreview.d.ts.map +1 -1
  92. package/dist/umd/helpers/index.d.ts +6 -6
  93. package/dist/umd/helpers/index.d.ts.map +1 -1
  94. package/dist/umd/helpers/viz-area-click/area-builder.d.ts.map +1 -1
  95. package/dist/umd/hooks/index.d.ts +0 -1
  96. package/dist/umd/hooks/index.d.ts.map +1 -1
  97. package/dist/umd/hooks/view-context/useHeatmapDataContext.d.ts +1 -0
  98. package/dist/umd/hooks/view-context/useHeatmapDataContext.d.ts.map +1 -1
  99. package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
  100. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  101. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts +0 -3
  102. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  103. package/dist/umd/index.js +2 -2
  104. package/dist/umd/{helpers/iframe-helper → libs/iframe-processor}/index.d.ts.map +1 -1
  105. package/dist/umd/libs/iframe-processor/lifecycle.d.ts +10 -0
  106. package/dist/umd/libs/iframe-processor/lifecycle.d.ts.map +1 -0
  107. package/dist/{esm/helpers/iframe-helper/iframe-fixer.d.ts → umd/libs/iframe-processor/orchestrator.d.ts} +1 -1
  108. package/dist/{esm/helpers/iframe-helper/iframe-fixer.d.ts.map → umd/libs/iframe-processor/orchestrator.d.ts.map} +1 -1
  109. package/dist/umd/libs/iframe-processor/processors/height-observer/index.d.ts +20 -0
  110. package/dist/umd/libs/iframe-processor/processors/height-observer/index.d.ts.map +1 -0
  111. package/dist/umd/libs/iframe-processor/processors/height-observer/observers.d.ts +7 -0
  112. package/dist/umd/libs/iframe-processor/processors/height-observer/observers.d.ts.map +1 -0
  113. package/dist/umd/libs/iframe-processor/processors/height-observer/types.d.ts +10 -0
  114. package/dist/umd/libs/iframe-processor/processors/height-observer/types.d.ts.map +1 -0
  115. package/dist/umd/libs/iframe-processor/processors/navigation/blockers.d.ts +9 -0
  116. package/dist/umd/libs/iframe-processor/processors/navigation/blockers.d.ts.map +1 -0
  117. package/dist/umd/libs/iframe-processor/processors/navigation/index.d.ts +20 -0
  118. package/dist/umd/libs/iframe-processor/processors/navigation/index.d.ts.map +1 -0
  119. package/dist/umd/libs/iframe-processor/processors/navigation/listeners.d.ts +12 -0
  120. package/dist/umd/libs/iframe-processor/processors/navigation/listeners.d.ts.map +1 -0
  121. package/dist/umd/libs/iframe-processor/processors/navigation/types.d.ts +7 -0
  122. package/dist/umd/libs/iframe-processor/processors/navigation/types.d.ts.map +1 -0
  123. package/dist/umd/libs/iframe-processor/processors/viewport/dimensions.d.ts +7 -0
  124. package/dist/umd/libs/iframe-processor/processors/viewport/dimensions.d.ts.map +1 -0
  125. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/gempages-swiper/fixes.d.ts +4 -0
  126. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/gempages-swiper/fixes.d.ts.map +1 -0
  127. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/gempages-swiper/index.d.ts +2 -0
  128. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/gempages-swiper/index.d.ts.map +1 -0
  129. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/gp-v7-slider/fixes.d.ts +3 -0
  130. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/gp-v7-slider/fixes.d.ts.map +1 -0
  131. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/gp-v7-slider/index.d.ts +2 -0
  132. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/gp-v7-slider/index.d.ts.map +1 -0
  133. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/viewport-unit-replacer/fixes.d.ts +7 -0
  134. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/viewport-unit-replacer/fixes.d.ts.map +1 -0
  135. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/viewport-unit-replacer/index.d.ts +2 -0
  136. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/fixes/viewport-unit-replacer/index.d.ts.map +1 -0
  137. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/index.d.ts +16 -0
  138. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/index.d.ts.map +1 -0
  139. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/registry.d.ts +14 -0
  140. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/registry.d.ts.map +1 -0
  141. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/types.d.ts +46 -0
  142. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/types.d.ts.map +1 -0
  143. package/dist/umd/{helpers/iframe-helper/iframe-viewport-replacer.d.ts → libs/iframe-processor/processors/viewport/index.d.ts} +2 -7
  144. package/dist/umd/libs/iframe-processor/processors/viewport/index.d.ts.map +1 -0
  145. package/dist/umd/libs/iframe-processor/processors/viewport/listeners.d.ts +3 -0
  146. package/dist/umd/libs/iframe-processor/processors/viewport/listeners.d.ts.map +1 -0
  147. package/dist/umd/libs/iframe-processor/processors/viewport/pipeline.d.ts +17 -0
  148. package/dist/umd/libs/iframe-processor/processors/viewport/pipeline.d.ts.map +1 -0
  149. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/index.d.ts +17 -0
  150. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/index.d.ts.map +1 -0
  151. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/registry.d.ts +16 -0
  152. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/registry.d.ts.map +1 -0
  153. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/shops/shop-566240210141053597/fixes.d.ts +11 -0
  154. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/shops/shop-566240210141053597/fixes.d.ts.map +1 -0
  155. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/shops/shop-566240210141053597/index.d.ts +2 -0
  156. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/shops/shop-566240210141053597/index.d.ts.map +1 -0
  157. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts +53 -0
  158. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts.map +1 -0
  159. package/dist/umd/{helpers/iframe-helper/iframe-style-enforcer.d.ts → libs/iframe-processor/processors/viewport/style-enforcer.d.ts} +2 -2
  160. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer.d.ts.map +1 -0
  161. package/dist/umd/libs/index.d.ts +1 -3
  162. package/dist/umd/libs/index.d.ts.map +1 -1
  163. package/dist/umd/libs/visualizer/AttentionMapRenderer.d.ts.map +1 -0
  164. package/dist/umd/libs/visualizer/ClickHeatmapRenderer.d.ts.map +1 -0
  165. package/dist/umd/libs/{GXVisualizer.d.ts → visualizer/GXVisualizer.d.ts} +0 -2
  166. package/dist/umd/libs/visualizer/GXVisualizer.d.ts.map +1 -0
  167. package/dist/umd/libs/visualizer/ScrollHeatmapRenderer.d.ts.map +1 -0
  168. package/dist/umd/libs/visualizer/index.d.ts +4 -0
  169. package/dist/umd/libs/visualizer/index.d.ts.map +1 -0
  170. package/dist/umd/libs/visualizer/types.d.ts.map +1 -0
  171. package/dist/umd/types/iframe-helper.d.ts +4 -0
  172. package/dist/umd/types/iframe-helper.d.ts.map +1 -1
  173. package/dist/umd/{helpers/dom-utils.d.ts → utils/dom.d.ts} +1 -1
  174. package/dist/umd/utils/dom.d.ts.map +1 -0
  175. package/dist/umd/utils/index.d.ts +9 -0
  176. package/dist/umd/utils/index.d.ts.map +1 -0
  177. package/dist/umd/utils/logger.d.ts.map +1 -0
  178. package/dist/umd/utils/observable.d.ts.map +1 -0
  179. package/dist/umd/utils/throttle.d.ts.map +1 -0
  180. package/package.json +1 -1
  181. package/dist/esm/helpers/dom-utils.d.ts.map +0 -1
  182. package/dist/esm/helpers/iframe-helper/iframe-height-observer.d.ts +0 -35
  183. package/dist/esm/helpers/iframe-helper/iframe-height-observer.d.ts.map +0 -1
  184. package/dist/esm/helpers/iframe-helper/iframe-navigation-blocker.d.ts +0 -52
  185. package/dist/esm/helpers/iframe-helper/iframe-navigation-blocker.d.ts.map +0 -1
  186. package/dist/esm/helpers/iframe-helper/iframe-style-enforcer.d.ts.map +0 -1
  187. package/dist/esm/helpers/iframe-helper/iframe-viewport-replacer.d.ts.map +0 -1
  188. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/base-style-replacer.d.ts +0 -61
  189. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/base-style-replacer.d.ts.map +0 -1
  190. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/flickity-fixer.d.ts +0 -10
  191. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/flickity-fixer.d.ts.map +0 -1
  192. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/index.d.ts +0 -9
  193. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/index.d.ts.map +0 -1
  194. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/observer-manager.d.ts +0 -14
  195. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/observer-manager.d.ts.map +0 -1
  196. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/sticky-cart-detector.d.ts +0 -12
  197. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/sticky-cart-detector.d.ts.map +0 -1
  198. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/swiper-fixer.d.ts +0 -12
  199. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base/swiper-fixer.d.ts.map +0 -1
  200. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/attribute-functions.d.ts +0 -9
  201. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/attribute-functions.d.ts.map +0 -1
  202. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/class-style-functions.d.ts +0 -29
  203. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/class-style-functions.d.ts.map +0 -1
  204. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/constants.d.ts +0 -24
  205. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/constants.d.ts.map +0 -1
  206. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/context-interface.d.ts +0 -29
  207. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/context-interface.d.ts.map +0 -1
  208. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/display-functions.d.ts +0 -17
  209. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/display-functions.d.ts.map +0 -1
  210. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/dom-injection-functions.d.ts +0 -17
  211. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/dom-injection-functions.d.ts.map +0 -1
  212. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/function-binder.d.ts +0 -33
  213. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/function-binder.d.ts.map +0 -1
  214. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/index.d.ts +0 -11
  215. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/index.d.ts.map +0 -1
  216. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/types.d.ts +0 -18
  217. package/dist/esm/helpers/iframe-helper/screenshot-fixes/base-functions/types.d.ts.map +0 -1
  218. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/content/content-functions.d.ts +0 -13
  219. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/content/content-functions.d.ts.map +0 -1
  220. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/images/image-functions.d.ts +0 -21
  221. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/images/image-functions.d.ts.map +0 -1
  222. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/index.d.ts +0 -11
  223. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/index.d.ts.map +0 -1
  224. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/layout/layout-functions.d.ts +0 -29
  225. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/layout/layout-functions.d.ts.map +0 -1
  226. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/popups/popup-functions.d.ts +0 -21
  227. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/popups/popup-functions.d.ts.map +0 -1
  228. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/shopify/shopify-functions.d.ts +0 -25
  229. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/shopify/shopify-functions.d.ts.map +0 -1
  230. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/sliders/slider-functions.d.ts +0 -29
  231. package/dist/esm/helpers/iframe-helper/screenshot-fixes/case-specific-functions/sliders/slider-functions.d.ts.map +0 -1
  232. package/dist/esm/helpers/iframe-helper/screenshot-fixes/index.d.ts +0 -7
  233. package/dist/esm/helpers/iframe-helper/screenshot-fixes/index.d.ts.map +0 -1
  234. package/dist/esm/helpers/iframe-helper/screenshot-fixes/init.d.ts +0 -9
  235. package/dist/esm/helpers/iframe-helper/screenshot-fixes/init.d.ts.map +0 -1
  236. package/dist/esm/helpers/iframe-helper/screenshot-fixes/mappings/desktop-mappings.d.ts +0 -7
  237. package/dist/esm/helpers/iframe-helper/screenshot-fixes/mappings/desktop-mappings.d.ts.map +0 -1
  238. package/dist/esm/helpers/iframe-helper/screenshot-fixes/mappings/mobile-mappings.d.ts +0 -7
  239. package/dist/esm/helpers/iframe-helper/screenshot-fixes/mappings/mobile-mappings.d.ts.map +0 -1
  240. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/add-dad-fuel5reasons-section-missing-slider-classes.d.ts +0 -6
  241. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/add-dad-fuel5reasons-section-missing-slider-classes.d.ts.map +0 -1
  242. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/fix-jocko-fuel-featured-stories-section.d.ts +0 -6
  243. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/fix-jocko-fuel-featured-stories-section.d.ts.map +0 -1
  244. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/index.d.ts +0 -19
  245. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/index.d.ts.map +0 -1
  246. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-balance-coffee-newsletter-form-container-display.d.ts +0 -6
  247. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-balance-coffee-newsletter-form-container-display.d.ts.map +0 -1
  248. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-boom-beauty-mobile-nav-menu-drawer-height.d.ts +0 -6
  249. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-boom-beauty-mobile-nav-menu-drawer-height.d.ts.map +0 -1
  250. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-cookie-diet-footer-newsletter-form-display.d.ts +0 -6
  251. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-cookie-diet-footer-newsletter-form-display.d.ts.map +0 -1
  252. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-dad-fuel-ingredient-highlights-section-image.d.ts +0 -6
  253. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-dad-fuel-ingredient-highlights-section-image.d.ts.map +0 -1
  254. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-get-thin-md-weight-loss-form-element-display.d.ts +0 -3
  255. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-get-thin-md-weight-loss-form-element-display.d.ts.map +0 -1
  256. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-mag-bak-add-on-toggle-display.d.ts +0 -6
  257. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-mag-bak-add-on-toggle-display.d.ts.map +0 -1
  258. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-pup-labs-mobile-search-opacity.d.ts +0 -6
  259. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-pup-labs-mobile-search-opacity.d.ts.map +0 -1
  260. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-rinse-bath-and-body-header-left-icons-display.d.ts +0 -6
  261. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-rinse-bath-and-body-header-left-icons-display.d.ts.map +0 -1
  262. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-try-snow-header-menu-icon-display.d.ts +0 -6
  263. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-try-snow-header-menu-icon-display.d.ts.map +0 -1
  264. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-bomix-mobile-mega-menu-width.d.ts +0 -6
  265. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-bomix-mobile-mega-menu-width.d.ts.map +0 -1
  266. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-jocko-product-page-swiper-sliders.d.ts +0 -6
  267. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-jocko-product-page-swiper-sliders.d.ts.map +0 -1
  268. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-vita-coco-slick-slider-inactive-slide-opacity.d.ts +0 -6
  269. package/dist/esm/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-vita-coco-slick-slider-inactive-slide-opacity.d.ts.map +0 -1
  270. package/dist/esm/helpers/iframe-helper/screenshot-fixes/screenshot-fixes-main.d.ts +0 -220
  271. package/dist/esm/helpers/iframe-helper/screenshot-fixes/screenshot-fixes-main.d.ts.map +0 -1
  272. package/dist/esm/helpers/iframe-helper/screenshot-fixes/screenshot-fixes-refactored.d.ts +0 -58
  273. package/dist/esm/helpers/iframe-helper/screenshot-fixes/screenshot-fixes-refactored.d.ts.map +0 -1
  274. package/dist/esm/helpers/iframe-helper/screenshot-fixes/third-party-apps/anita-screenshot-fixes.d.ts +0 -589
  275. package/dist/esm/helpers/iframe-helper/screenshot-fixes/third-party-apps/anita-screenshot-fixes.d.ts.map +0 -1
  276. package/dist/esm/helpers/iframe-helper/screenshot-fixes/third-party-apps/forson-screenshot-fixes.d.ts +0 -57
  277. package/dist/esm/helpers/iframe-helper/screenshot-fixes/third-party-apps/forson-screenshot-fixes.d.ts.map +0 -1
  278. package/dist/esm/helpers/iframe-helper/screenshot-fixes/third-party-apps/general-site-fixes.d.ts +0 -88
  279. package/dist/esm/helpers/iframe-helper/screenshot-fixes/third-party-apps/general-site-fixes.d.ts.map +0 -1
  280. package/dist/esm/helpers/iframe-helper/screenshot-fixes/third-party-apps/index.d.ts +0 -15
  281. package/dist/esm/helpers/iframe-helper/screenshot-fixes/third-party-apps/index.d.ts.map +0 -1
  282. package/dist/esm/helpers/iframe-helper/screenshot-fixes/third-party-apps/loading-background-fixes.d.ts +0 -24
  283. package/dist/esm/helpers/iframe-helper/screenshot-fixes/third-party-apps/loading-background-fixes.d.ts.map +0 -1
  284. package/dist/esm/helpers/iframe-helper/screenshot-fixes/types.d.ts +0 -70
  285. package/dist/esm/helpers/iframe-helper/screenshot-fixes/types.d.ts.map +0 -1
  286. package/dist/esm/helpers/iframe-helper/start.d.ts +0 -58
  287. package/dist/esm/helpers/iframe-helper/start.d.ts.map +0 -1
  288. package/dist/esm/helpers/iframe-helper/style-fixer/base-replacer.d.ts +0 -54
  289. package/dist/esm/helpers/iframe-helper/style-fixer/base-replacer.d.ts.map +0 -1
  290. package/dist/esm/helpers/iframe-helper/style-fixer/general-fixer.d.ts +0 -54
  291. package/dist/esm/helpers/iframe-helper/style-fixer/general-fixer.d.ts.map +0 -1
  292. package/dist/esm/helpers/iframe-helper/style-fixer/index.d.ts +0 -12
  293. package/dist/esm/helpers/iframe-helper/style-fixer/index.d.ts.map +0 -1
  294. package/dist/esm/helpers/iframe-helper/style-fixer/main-fixer.d.ts +0 -32
  295. package/dist/esm/helpers/iframe-helper/style-fixer/main-fixer.d.ts.map +0 -1
  296. package/dist/esm/helpers/iframe-helper/style-fixer/observer-manager.d.ts +0 -14
  297. package/dist/esm/helpers/iframe-helper/style-fixer/observer-manager.d.ts.map +0 -1
  298. package/dist/esm/helpers/iframe-helper/style-fixer/site-specific-replacer.d.ts +0 -25
  299. package/dist/esm/helpers/iframe-helper/style-fixer/site-specific-replacer.d.ts.map +0 -1
  300. package/dist/esm/helpers/iframe-helper/style-fixer/sticky-detector.d.ts +0 -12
  301. package/dist/esm/helpers/iframe-helper/style-fixer/sticky-detector.d.ts.map +0 -1
  302. package/dist/esm/helpers/iframe-helper/style-fixer/types.d.ts +0 -40
  303. package/dist/esm/helpers/iframe-helper/style-fixer/types.d.ts.map +0 -1
  304. package/dist/esm/helpers/logger.d.ts.map +0 -1
  305. package/dist/esm/helpers/observable.d.ts.map +0 -1
  306. package/dist/esm/helpers/throttle.d.ts.map +0 -1
  307. package/dist/esm/hooks/viz-iframe/index.d.ts +0 -2
  308. package/dist/esm/hooks/viz-iframe/index.d.ts.map +0 -1
  309. package/dist/esm/hooks/viz-iframe/useIframeHeightProcessor.d.ts +0 -49
  310. package/dist/esm/hooks/viz-iframe/useIframeHeightProcessor.d.ts.map +0 -1
  311. package/dist/esm/libs/AttentionMapRenderer.d.ts.map +0 -1
  312. package/dist/esm/libs/ClickHeatmapRenderer.d.ts.map +0 -1
  313. package/dist/esm/libs/GXVisualizer.d.ts.map +0 -1
  314. package/dist/esm/libs/ScrollHeatmapRenderer.d.ts.map +0 -1
  315. package/dist/esm/libs/types.d.ts.map +0 -1
  316. package/dist/umd/helpers/dom-utils.d.ts.map +0 -1
  317. package/dist/umd/helpers/iframe-helper/iframe-height-observer.d.ts +0 -35
  318. package/dist/umd/helpers/iframe-helper/iframe-height-observer.d.ts.map +0 -1
  319. package/dist/umd/helpers/iframe-helper/iframe-navigation-blocker.d.ts +0 -52
  320. package/dist/umd/helpers/iframe-helper/iframe-navigation-blocker.d.ts.map +0 -1
  321. package/dist/umd/helpers/iframe-helper/iframe-style-enforcer.d.ts.map +0 -1
  322. package/dist/umd/helpers/iframe-helper/iframe-viewport-replacer.d.ts.map +0 -1
  323. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/base-style-replacer.d.ts +0 -61
  324. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/base-style-replacer.d.ts.map +0 -1
  325. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/flickity-fixer.d.ts +0 -10
  326. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/flickity-fixer.d.ts.map +0 -1
  327. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/index.d.ts +0 -9
  328. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/index.d.ts.map +0 -1
  329. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/observer-manager.d.ts +0 -14
  330. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/observer-manager.d.ts.map +0 -1
  331. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/sticky-cart-detector.d.ts +0 -12
  332. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/sticky-cart-detector.d.ts.map +0 -1
  333. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/swiper-fixer.d.ts +0 -12
  334. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base/swiper-fixer.d.ts.map +0 -1
  335. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/attribute-functions.d.ts +0 -9
  336. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/attribute-functions.d.ts.map +0 -1
  337. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/class-style-functions.d.ts +0 -29
  338. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/class-style-functions.d.ts.map +0 -1
  339. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/constants.d.ts +0 -24
  340. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/constants.d.ts.map +0 -1
  341. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/context-interface.d.ts +0 -29
  342. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/context-interface.d.ts.map +0 -1
  343. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/display-functions.d.ts +0 -17
  344. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/display-functions.d.ts.map +0 -1
  345. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/dom-injection-functions.d.ts +0 -17
  346. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/dom-injection-functions.d.ts.map +0 -1
  347. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/function-binder.d.ts +0 -33
  348. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/function-binder.d.ts.map +0 -1
  349. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/index.d.ts +0 -11
  350. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/index.d.ts.map +0 -1
  351. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/types.d.ts +0 -18
  352. package/dist/umd/helpers/iframe-helper/screenshot-fixes/base-functions/types.d.ts.map +0 -1
  353. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/content/content-functions.d.ts +0 -13
  354. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/content/content-functions.d.ts.map +0 -1
  355. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/images/image-functions.d.ts +0 -21
  356. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/images/image-functions.d.ts.map +0 -1
  357. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/index.d.ts +0 -11
  358. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/index.d.ts.map +0 -1
  359. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/layout/layout-functions.d.ts +0 -29
  360. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/layout/layout-functions.d.ts.map +0 -1
  361. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/popups/popup-functions.d.ts +0 -21
  362. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/popups/popup-functions.d.ts.map +0 -1
  363. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/shopify/shopify-functions.d.ts +0 -25
  364. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/shopify/shopify-functions.d.ts.map +0 -1
  365. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/sliders/slider-functions.d.ts +0 -29
  366. package/dist/umd/helpers/iframe-helper/screenshot-fixes/case-specific-functions/sliders/slider-functions.d.ts.map +0 -1
  367. package/dist/umd/helpers/iframe-helper/screenshot-fixes/index.d.ts +0 -7
  368. package/dist/umd/helpers/iframe-helper/screenshot-fixes/index.d.ts.map +0 -1
  369. package/dist/umd/helpers/iframe-helper/screenshot-fixes/init.d.ts +0 -9
  370. package/dist/umd/helpers/iframe-helper/screenshot-fixes/init.d.ts.map +0 -1
  371. package/dist/umd/helpers/iframe-helper/screenshot-fixes/mappings/desktop-mappings.d.ts +0 -7
  372. package/dist/umd/helpers/iframe-helper/screenshot-fixes/mappings/desktop-mappings.d.ts.map +0 -1
  373. package/dist/umd/helpers/iframe-helper/screenshot-fixes/mappings/mobile-mappings.d.ts +0 -7
  374. package/dist/umd/helpers/iframe-helper/screenshot-fixes/mappings/mobile-mappings.d.ts.map +0 -1
  375. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/add-dad-fuel5reasons-section-missing-slider-classes.d.ts +0 -6
  376. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/add-dad-fuel5reasons-section-missing-slider-classes.d.ts.map +0 -1
  377. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/fix-jocko-fuel-featured-stories-section.d.ts +0 -6
  378. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/fix-jocko-fuel-featured-stories-section.d.ts.map +0 -1
  379. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/index.d.ts +0 -19
  380. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/index.d.ts.map +0 -1
  381. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-balance-coffee-newsletter-form-container-display.d.ts +0 -6
  382. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-balance-coffee-newsletter-form-container-display.d.ts.map +0 -1
  383. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-boom-beauty-mobile-nav-menu-drawer-height.d.ts +0 -6
  384. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-boom-beauty-mobile-nav-menu-drawer-height.d.ts.map +0 -1
  385. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-cookie-diet-footer-newsletter-form-display.d.ts +0 -6
  386. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-cookie-diet-footer-newsletter-form-display.d.ts.map +0 -1
  387. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-dad-fuel-ingredient-highlights-section-image.d.ts +0 -6
  388. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-dad-fuel-ingredient-highlights-section-image.d.ts.map +0 -1
  389. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-get-thin-md-weight-loss-form-element-display.d.ts +0 -3
  390. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-get-thin-md-weight-loss-form-element-display.d.ts.map +0 -1
  391. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-mag-bak-add-on-toggle-display.d.ts +0 -6
  392. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-mag-bak-add-on-toggle-display.d.ts.map +0 -1
  393. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-pup-labs-mobile-search-opacity.d.ts +0 -6
  394. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-pup-labs-mobile-search-opacity.d.ts.map +0 -1
  395. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-rinse-bath-and-body-header-left-icons-display.d.ts +0 -6
  396. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-rinse-bath-and-body-header-left-icons-display.d.ts.map +0 -1
  397. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-try-snow-header-menu-icon-display.d.ts +0 -6
  398. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/remove-try-snow-header-menu-icon-display.d.ts.map +0 -1
  399. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-bomix-mobile-mega-menu-width.d.ts +0 -6
  400. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-bomix-mobile-mega-menu-width.d.ts.map +0 -1
  401. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-jocko-product-page-swiper-sliders.d.ts +0 -6
  402. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-jocko-product-page-swiper-sliders.d.ts.map +0 -1
  403. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-vita-coco-slick-slider-inactive-slide-opacity.d.ts +0 -6
  404. package/dist/umd/helpers/iframe-helper/screenshot-fixes/methods/mobile/set-vita-coco-slick-slider-inactive-slide-opacity.d.ts.map +0 -1
  405. package/dist/umd/helpers/iframe-helper/screenshot-fixes/screenshot-fixes-main.d.ts +0 -220
  406. package/dist/umd/helpers/iframe-helper/screenshot-fixes/screenshot-fixes-main.d.ts.map +0 -1
  407. package/dist/umd/helpers/iframe-helper/screenshot-fixes/screenshot-fixes-refactored.d.ts +0 -58
  408. package/dist/umd/helpers/iframe-helper/screenshot-fixes/screenshot-fixes-refactored.d.ts.map +0 -1
  409. package/dist/umd/helpers/iframe-helper/screenshot-fixes/third-party-apps/anita-screenshot-fixes.d.ts +0 -589
  410. package/dist/umd/helpers/iframe-helper/screenshot-fixes/third-party-apps/anita-screenshot-fixes.d.ts.map +0 -1
  411. package/dist/umd/helpers/iframe-helper/screenshot-fixes/third-party-apps/forson-screenshot-fixes.d.ts +0 -57
  412. package/dist/umd/helpers/iframe-helper/screenshot-fixes/third-party-apps/forson-screenshot-fixes.d.ts.map +0 -1
  413. package/dist/umd/helpers/iframe-helper/screenshot-fixes/third-party-apps/general-site-fixes.d.ts +0 -88
  414. package/dist/umd/helpers/iframe-helper/screenshot-fixes/third-party-apps/general-site-fixes.d.ts.map +0 -1
  415. package/dist/umd/helpers/iframe-helper/screenshot-fixes/third-party-apps/index.d.ts +0 -15
  416. package/dist/umd/helpers/iframe-helper/screenshot-fixes/third-party-apps/index.d.ts.map +0 -1
  417. package/dist/umd/helpers/iframe-helper/screenshot-fixes/third-party-apps/loading-background-fixes.d.ts +0 -24
  418. package/dist/umd/helpers/iframe-helper/screenshot-fixes/third-party-apps/loading-background-fixes.d.ts.map +0 -1
  419. package/dist/umd/helpers/iframe-helper/screenshot-fixes/types.d.ts +0 -70
  420. package/dist/umd/helpers/iframe-helper/screenshot-fixes/types.d.ts.map +0 -1
  421. package/dist/umd/helpers/iframe-helper/start.d.ts +0 -58
  422. package/dist/umd/helpers/iframe-helper/start.d.ts.map +0 -1
  423. package/dist/umd/helpers/iframe-helper/style-fixer/base-replacer.d.ts +0 -54
  424. package/dist/umd/helpers/iframe-helper/style-fixer/base-replacer.d.ts.map +0 -1
  425. package/dist/umd/helpers/iframe-helper/style-fixer/general-fixer.d.ts +0 -54
  426. package/dist/umd/helpers/iframe-helper/style-fixer/general-fixer.d.ts.map +0 -1
  427. package/dist/umd/helpers/iframe-helper/style-fixer/index.d.ts +0 -12
  428. package/dist/umd/helpers/iframe-helper/style-fixer/index.d.ts.map +0 -1
  429. package/dist/umd/helpers/iframe-helper/style-fixer/main-fixer.d.ts +0 -32
  430. package/dist/umd/helpers/iframe-helper/style-fixer/main-fixer.d.ts.map +0 -1
  431. package/dist/umd/helpers/iframe-helper/style-fixer/observer-manager.d.ts +0 -14
  432. package/dist/umd/helpers/iframe-helper/style-fixer/observer-manager.d.ts.map +0 -1
  433. package/dist/umd/helpers/iframe-helper/style-fixer/site-specific-replacer.d.ts +0 -25
  434. package/dist/umd/helpers/iframe-helper/style-fixer/site-specific-replacer.d.ts.map +0 -1
  435. package/dist/umd/helpers/iframe-helper/style-fixer/sticky-detector.d.ts +0 -12
  436. package/dist/umd/helpers/iframe-helper/style-fixer/sticky-detector.d.ts.map +0 -1
  437. package/dist/umd/helpers/iframe-helper/style-fixer/types.d.ts +0 -40
  438. package/dist/umd/helpers/iframe-helper/style-fixer/types.d.ts.map +0 -1
  439. package/dist/umd/helpers/logger.d.ts.map +0 -1
  440. package/dist/umd/helpers/observable.d.ts.map +0 -1
  441. package/dist/umd/helpers/throttle.d.ts.map +0 -1
  442. package/dist/umd/hooks/viz-iframe/index.d.ts +0 -2
  443. package/dist/umd/hooks/viz-iframe/index.d.ts.map +0 -1
  444. package/dist/umd/hooks/viz-iframe/useIframeHeightProcessor.d.ts +0 -49
  445. package/dist/umd/hooks/viz-iframe/useIframeHeightProcessor.d.ts.map +0 -1
  446. package/dist/umd/libs/AttentionMapRenderer.d.ts.map +0 -1
  447. package/dist/umd/libs/ClickHeatmapRenderer.d.ts.map +0 -1
  448. package/dist/umd/libs/GXVisualizer.d.ts.map +0 -1
  449. package/dist/umd/libs/ScrollHeatmapRenderer.d.ts.map +0 -1
  450. package/dist/umd/libs/types.d.ts.map +0 -1
  451. /package/dist/esm/{helpers/iframe-helper → libs/iframe-processor}/index.d.ts +0 -0
  452. /package/dist/esm/libs/{AttentionMapRenderer.d.ts → visualizer/AttentionMapRenderer.d.ts} +0 -0
  453. /package/dist/esm/libs/{ClickHeatmapRenderer.d.ts → visualizer/ClickHeatmapRenderer.d.ts} +0 -0
  454. /package/dist/esm/libs/{ScrollHeatmapRenderer.d.ts → visualizer/ScrollHeatmapRenderer.d.ts} +0 -0
  455. /package/dist/esm/libs/{types.d.ts → visualizer/types.d.ts} +0 -0
  456. /package/dist/esm/{helpers → utils}/logger.d.ts +0 -0
  457. /package/dist/esm/{helpers → utils}/observable.d.ts +0 -0
  458. /package/dist/esm/{helpers → utils}/throttle.d.ts +0 -0
  459. /package/dist/umd/{helpers/iframe-helper → libs/iframe-processor}/index.d.ts +0 -0
  460. /package/dist/umd/libs/{AttentionMapRenderer.d.ts → visualizer/AttentionMapRenderer.d.ts} +0 -0
  461. /package/dist/umd/libs/{ClickHeatmapRenderer.d.ts → visualizer/ClickHeatmapRenderer.d.ts} +0 -0
  462. /package/dist/umd/libs/{ScrollHeatmapRenderer.d.ts → visualizer/ScrollHeatmapRenderer.d.ts} +0 -0
  463. /package/dist/umd/libs/{types.d.ts → visualizer/types.d.ts} +0 -0
  464. /package/dist/umd/{helpers → utils}/logger.d.ts +0 -0
  465. /package/dist/umd/{helpers → utils}/observable.d.ts +0 -0
  466. /package/dist/umd/{helpers → utils}/throttle.d.ts +0 -0
package/dist/esm/index.js CHANGED
@@ -1195,6 +1195,7 @@ const useHeatmapDataContext = createViewContextHook({
1195
1195
  scrollmap: store.scrollmap.get(viewId),
1196
1196
  attentionMap: store.attentionMap.get(viewId),
1197
1197
  dataInfo: store.dataInfo.get(viewId),
1198
+ isEmptyData: !store.data.get(viewId) || store.data.get(viewId)?.length === 0,
1198
1199
  }),
1199
1200
  getActions: (store, viewId) => ({
1200
1201
  setData: (newData) => store.setData(newData, viewId),
@@ -1482,32 +1483,57 @@ const useRegisterHeatmap = ({ clickmap, scrollmap, clickAreas, attentionMap }) =
1482
1483
  };
1483
1484
 
1484
1485
  /**
1485
- * Create an observable value with subscribe/unsubscribe pattern
1486
+ * Draw a backdrop overlay on canvas with a cutout for the active element
1487
+ * This creates a "spotlight" effect highlighting the active element
1486
1488
  */
1487
- function createObservable(initialValue) {
1488
- const subscribers = new Set();
1489
- const observable = {
1490
- value: initialValue,
1491
- observe: (callback) => {
1492
- subscribers.add(callback);
1493
- // Immediately call with current value
1494
- if (observable.value !== undefined) {
1495
- callback(observable.value);
1496
- }
1497
- },
1498
- unobserve: (callback) => {
1499
- subscribers.delete(callback);
1500
- },
1501
- update: (newValue) => {
1502
- observable.value = newValue;
1503
- // Notify all subscribers
1504
- subscribers.forEach((callback) => {
1505
- callback(newValue);
1506
- });
1507
- },
1508
- };
1509
- return observable;
1510
- }
1489
+ const drawBackdropWithCutout = (options) => {
1490
+ const { canvas, activeRect, backdropColor = '#000000', backdropOpacity = 0.5, cutoutExpansion = 0 } = options;
1491
+ const ctx = canvas.getContext('2d');
1492
+ if (!ctx)
1493
+ return;
1494
+ const { width: canvasWidth, height: canvasHeight } = canvas;
1495
+ // Apply expansion to the cutout rect
1496
+ const top = Math.max(0, activeRect.top - cutoutExpansion);
1497
+ const left = Math.max(0, activeRect.left - cutoutExpansion);
1498
+ const width = Math.min(canvasWidth - left, activeRect.width + cutoutExpansion * 2);
1499
+ const height = Math.min(canvasHeight - top, activeRect.height + cutoutExpansion * 2);
1500
+ // Clear previous drawing
1501
+ ctx.clearRect(0, 0, canvasWidth, canvasHeight);
1502
+ // Set backdrop style
1503
+ ctx.fillStyle = backdropColor;
1504
+ ctx.globalAlpha = backdropOpacity;
1505
+ // Draw backdrop in 4 rectangles around the active element
1506
+ // This creates a cutout effect
1507
+ // Top rectangle (above active element)
1508
+ if (top > 0) {
1509
+ ctx.fillRect(0, 0, canvasWidth, top);
1510
+ }
1511
+ // Bottom rectangle (below active element)
1512
+ const bottomY = top + height;
1513
+ if (bottomY < canvasHeight) {
1514
+ ctx.fillRect(0, bottomY, canvasWidth, canvasHeight - bottomY);
1515
+ }
1516
+ // Left rectangle (left of active element)
1517
+ if (left > 0) {
1518
+ ctx.fillRect(0, top, left, height);
1519
+ }
1520
+ // Right rectangle (right of active element)
1521
+ const rightX = left + width;
1522
+ if (rightX < canvasWidth) {
1523
+ ctx.fillRect(rightX, top, canvasWidth - rightX, height);
1524
+ }
1525
+ // Reset alpha
1526
+ ctx.globalAlpha = 1.0;
1527
+ };
1528
+ /**
1529
+ * Clear the entire canvas
1530
+ */
1531
+ const clearCanvas = (canvas) => {
1532
+ const ctx = canvas.getContext('2d');
1533
+ if (!ctx)
1534
+ return;
1535
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1536
+ };
1511
1537
 
1512
1538
  function isElementInViewport(elementRect, visualRef, scale) {
1513
1539
  if (!elementRect)
@@ -1545,6 +1571,12 @@ function isElementRectInViewport(elementRect, visualRect, scale) {
1545
1571
  return elementTop > viewportTop && elementBottom < viewportBottom;
1546
1572
  }
1547
1573
 
1574
+ function isMobileDevice(userAgent) {
1575
+ if (!userAgent)
1576
+ return false;
1577
+ return /android|webos|iphone|ipad|ipod|blackberry|windows phone|opera mini|iemobile|mobile|silk|fennec|bada|tizen|symbian|nokia|palmsource|meego|sailfish|kindle|playbook|bb10|rim/i.test(userAgent);
1578
+ }
1579
+
1548
1580
  const CLARITY_HEATMAP_CANVAS_ID = 'clarity-heatmap-canvas';
1549
1581
  const HEATMAP_ELEMENT_ATTRIBUTE = 'data-clarity-hashalpha';
1550
1582
  function isIgnoredCanvas(element) {
@@ -1601,324 +1633,36 @@ function getElementHash(element) {
1601
1633
  return element.getAttribute('data-clarity-hashbeta') || element.getAttribute('data-clarity-hashalpha');
1602
1634
  }
1603
1635
 
1604
- const AREA_HOVER_BOX_SHADOW = '0 0 0 1px #0078D4, 0 0 0 1px #0078D4 inset, 0 0 0 2px white inset';
1605
- const AREA_HOVER_ELEMENT_ID = 'clarity-edit-hover';
1606
- const AREA_MAP_DIV_ATTRIBUTE = 'data-clarity-area-map-div';
1607
- const HEATMAP_AREA_CONTAINER_CLASS = 'heatmap-area-container';
1608
- const HEATMAP_AREA_CONTAINER_SELECTOR = `.${HEATMAP_AREA_CONTAINER_CLASS}`;
1609
- const AREA_CONTAINER_STYLES = `
1610
- position: absolute;
1611
- top: 0;
1612
- left: 0;
1613
- width: 100%;
1614
- height: 100%;
1615
- pointer-events: none;
1616
- z-index: 999999;
1617
- `;
1618
- const AREA_INNER_CONTAINER_STYLES = `
1619
- position: relative;
1620
- width: 100%;
1621
- height: 100%;
1622
- `;
1623
- const AREA_COLOR_GRADIENT = [
1624
- [0, 0, 255], // Blue
1625
- [0, 255, 255], // Cyan
1626
- [0, 255, 0], // Green
1627
- [255, 255, 0], // Yellow
1628
- [255, 0, 0], // Red
1629
- ];
1630
- const AREA_RENDERER_SELECTORS = {
1631
- containerAttribute: AREA_MAP_DIV_ATTRIBUTE,
1632
- containerSelector: `[${AREA_MAP_DIV_ATTRIBUTE}]`,
1633
- innerContainerClass: HEATMAP_AREA_CONTAINER_CLASS,
1634
- innerContainerSelector: HEATMAP_AREA_CONTAINER_SELECTOR,
1635
- };
1636
-
1637
- const CALLOUT_PADDING = 0;
1638
- const CALLOUT_ARROW_SIZE = 8;
1639
- const CALLOUT_OFFSET = { x: -8, y: 0 };
1640
- const CALLOUT_ALIGNMENT = 'left';
1641
- const CLICKED_ELEMENT_ID_BASE = 'gx-hm-clicked-element';
1642
- const SECONDARY_CLICKED_ELEMENT_ID_BASE = 'gx-hm-secondary-clicked-element';
1643
- const HOVERED_ELEMENT_ID_BASE = 'gx-hm-hovered-element';
1644
- const SECONDARY_HOVERED_ELEMENT_ID_BASE = 'gx-hm-secondary-hovered-element';
1645
- const DEFAULT_POSITION_MODE = 'absolute';
1646
-
1647
- /**
1648
- * Get color from click distribution percentage (0-100)
1649
- */
1650
- function getColorFromClickDist(clickDist) {
1651
- // Ensure clickDist is in range [0, 100]
1652
- const normalizedDist = Math.max(0, Math.min(100, clickDist));
1653
- // Calculate gradient index
1654
- const maxIndex = AREA_COLOR_GRADIENT.length - 1;
1655
- const index = Math.floor((normalizedDist / 100) * maxIndex);
1656
- const clampedIndex = Math.min(index, maxIndex);
1657
- const [r, g, b] = AREA_COLOR_GRADIENT[clampedIndex];
1658
- // Return rgba with 60% opacity
1659
- return `rgba(${r}, ${g}, ${b}, 0.6)`;
1660
- }
1661
- /**
1662
- * Get hover color (slightly lighter) from click distribution
1663
- */
1664
- function getHoverColorFromClickDist(clickDist) {
1665
- const normalizedDist = Math.max(0, Math.min(100, clickDist));
1666
- const maxIndex = AREA_COLOR_GRADIENT.length - 1;
1667
- const index = Math.floor((normalizedDist / 100) * maxIndex);
1668
- const clampedIndex = Math.min(index, maxIndex);
1669
- const [r, g, b] = AREA_COLOR_GRADIENT[clampedIndex];
1670
- // Return rgba with 80% opacity for hover
1671
- return `rgba(${r}, ${g}, ${b}, 0.8)`;
1672
- }
1673
- /**
1674
- * Calculate click distribution percentage from total clicks
1675
- */
1676
- function calculateClickDistribution(elementClicks, totalClicks) {
1677
- if (totalClicks === 0)
1678
- return 0;
1679
- return (elementClicks / totalClicks) * 100;
1680
- }
1681
-
1682
- function getElementRect(element, _shadowRoot) {
1683
- const rect = element.getBoundingClientRect();
1684
- const width = rect.width;
1685
- const height = rect.height;
1686
- const doc = element.ownerDocument || document;
1687
- const scrollTop = doc.documentElement?.scrollTop || doc.body?.scrollTop || 0;
1688
- const scrollLeft = doc.documentElement?.scrollLeft || doc.body?.scrollLeft || 0;
1689
- const top = rect.top + scrollTop;
1690
- const left = rect.left + scrollLeft;
1691
- const absoluteLeft = left;
1692
- const absoluteTop = top;
1693
- const absoluteRight = absoluteLeft + width;
1694
- const absoluteBottom = absoluteTop + height;
1695
- return {
1696
- width,
1697
- height,
1698
- top,
1699
- left,
1700
- absoluteLeft,
1701
- absoluteTop,
1702
- absoluteRight,
1703
- absoluteBottom,
1704
- outOfBounds: false,
1636
+ class Logger {
1637
+ config = {
1638
+ enabled: false,
1639
+ prefix: '',
1640
+ timestamp: false,
1705
1641
  };
1706
- }
1707
- function isElementFixed(element) {
1708
- if (getComputedStyle(element).position === 'fixed') {
1709
- return true;
1710
- }
1711
- if (element.nodeName === 'HTML') {
1712
- return false;
1642
+ /**
1643
+ * Cấu hình logger
1644
+ * @param config - Cấu hình logger
1645
+ */
1646
+ configure(config) {
1647
+ this.config = { ...this.config, ...config };
1713
1648
  }
1714
- const parent = element.parentElement;
1715
- return parent ? isElementFixed(parent) : false;
1716
- }
1717
- function doAreasOverlap(area1, area2) {
1718
- const r1 = area1.rect.value;
1719
- const r2 = area2.rect.value;
1720
- if (!r1 || !r2)
1721
- return false;
1722
- return ((r1.absoluteBottom > r2.absoluteTop &&
1723
- r1.absoluteTop < r2.absoluteBottom &&
1724
- r1.absoluteRight > r2.absoluteLeft &&
1725
- r1.absoluteLeft < r2.absoluteRight) ||
1726
- (r2.absoluteBottom > r1.absoluteTop &&
1727
- r2.absoluteTop < r1.absoluteBottom &&
1728
- r2.absoluteRight > r1.absoluteLeft &&
1729
- r2.absoluteLeft < r1.absoluteRight));
1730
- }
1731
- function isAreaContainedIn(area1, area2) {
1732
- const r1 = area1.rect.value;
1733
- const r2 = area2.rect.value;
1734
- if (!r1 || !r2)
1735
- return false;
1736
- return (r1.absoluteTop >= r2.absoluteTop &&
1737
- r1.absoluteBottom <= r2.absoluteBottom &&
1738
- r1.absoluteLeft >= r2.absoluteLeft &&
1739
- r1.absoluteRight <= r2.absoluteRight);
1740
- }
1741
- function isElementAncestorOf(ancestor, descendant, doc) {
1742
- return ancestor.contains(descendant);
1743
- }
1744
- function isElementSelectable(element, index, elements) {
1745
- if (isIgnoredCanvas(element)) {
1746
- return false;
1649
+ /**
1650
+ * Lấy cấu hình hiện tại
1651
+ */
1652
+ getConfig() {
1653
+ return { ...this.config };
1747
1654
  }
1748
- if (element.hasAttribute(AREA_MAP_DIV_ATTRIBUTE)) {
1749
- return false;
1655
+ /**
1656
+ * Bật logger
1657
+ */
1658
+ enable() {
1659
+ this.config.enabled = true;
1750
1660
  }
1751
- if (index === 0 && elements.length > 1 && element.nodeName === 'BODY') {
1752
- return false;
1753
- }
1754
- return true;
1755
- }
1756
- function sortAreasByClickDist(areas) {
1757
- return [...areas].sort((a, b) => {
1758
- if (a.clickDist !== b.clickDist) {
1759
- return b.clickDist - a.clickDist;
1760
- }
1761
- return b.totalclicks - a.totalclicks;
1762
- });
1763
- }
1764
- function isRectTooSmallForLabel(rect) {
1765
- return rect.width < 67 || rect.height < 30;
1766
- }
1767
-
1768
- function getElementSelector(element) {
1769
- if (element.id) {
1770
- return `#${element.id}`;
1771
- }
1772
- if (element.className) {
1773
- const classes = Array.from(element.classList).join('.');
1774
- if (classes) {
1775
- return `${element.tagName.toLowerCase()}.${classes}`;
1776
- }
1777
- }
1778
- return element.tagName.toLowerCase();
1779
- }
1780
- /**
1781
- * Calculate total clicks for an element including all its child elements
1782
- * @param element - The parent element
1783
- * @param elementMapInfo - Map of hash to element click info
1784
- * @returns Total clicks for element + all descendants
1785
- */
1786
- function calculateTotalClicksWithChildren(element, clickMapMetrics) {
1787
- let totalClicks = 0;
1788
- // Get clicks for the element itself
1789
- const elementHash = getElementHash(element);
1790
- if (elementHash) {
1791
- const elementInfo = clickMapMetrics[elementHash];
1792
- totalClicks += elementInfo?.totalClicked.value ?? 0;
1793
- }
1794
- const children = element.querySelectorAll('*');
1795
- children.forEach((child) => {
1796
- const childHash = getElementHash(child);
1797
- if (childHash) {
1798
- const childInfo = clickMapMetrics[childHash];
1799
- totalClicks += childInfo?.totalClicked.value ?? 0;
1800
- }
1801
- });
1802
- return totalClicks;
1803
- }
1804
- function buildAreaNode(element, hash, heatmapInfo, shadowRoot, persistedData) {
1805
- if (!heatmapInfo.clickMapMetrics)
1806
- return;
1807
- const totalClicks = heatmapInfo.totalClicks || 0;
1808
- const elementInfo = heatmapInfo.clickMapMetrics[hash];
1809
- // Calculate total clicks including all child elements
1810
- const elementClicks = calculateTotalClicksWithChildren(element, heatmapInfo.clickMapMetrics);
1811
- const clickDist = calculateClickDistribution(elementClicks, totalClicks);
1812
- const rect = getElementRect(element);
1813
- const color = getColorFromClickDist(clickDist);
1814
- const hoverColor = getHoverColorFromClickDist(clickDist);
1815
- const areaNode = {
1816
- kind: persistedData?.kind || 'area',
1817
- id: persistedData?.id || `${hash}_${Date.now()}`,
1818
- hash,
1819
- selector: persistedData?.selector || elementInfo?.selector || getElementSelector(element),
1820
- // DOM references
1821
- element,
1822
- areaElement: null,
1823
- shadowElement: null,
1824
- shadowStyleElement: null,
1825
- // Graph structure
1826
- parentNode: null,
1827
- childNodes: new Set(),
1828
- // Position
1829
- rect: createObservable(rect),
1830
- isFixed: isElementFixed(element),
1831
- priority: false,
1832
- // Click tracking
1833
- totalclicks: elementClicks,
1834
- cumulativeClicks: elementClicks,
1835
- cumulativeMaxClicks: totalClicks,
1836
- clickDist,
1837
- hasClickInfo: true,
1838
- // Visual
1839
- color,
1840
- hoverColor,
1841
- // Lifecycle
1842
- changeObserver: null,
1843
- };
1844
- return areaNode;
1845
- }
1846
- function getTopElementsByClicks(clickMapMetrics, topN = 10) {
1847
- const elements = Object.entries(clickMapMetrics)
1848
- .map(([hash, info]) => ({
1849
- hash,
1850
- totalclicks: info.totalClicked.value ?? 0,
1851
- selector: info.selector || '',
1852
- }))
1853
- .sort((a, b) => b.totalclicks - a.totalclicks)
1854
- .slice(0, topN);
1855
- return elements;
1856
- }
1857
-
1858
- /**
1859
- * Build parent-child relationships between areas based on DOM hierarchy
1860
- * @param areas - Array of area nodes to build relationships for
1861
- */
1862
- function buildAreaGraph(areas) {
1863
- // Clear existing relationships
1864
- areas.forEach((area) => {
1865
- area.parentNode = null;
1866
- area.childNodes.clear();
1867
- });
1868
- // Build relationships based on DOM containment
1869
- for (let i = 0; i < areas.length; i++) {
1870
- const area = areas[i];
1871
- for (let j = 0; j < areas.length; j++) {
1872
- if (i === j)
1873
- continue;
1874
- const otherArea = areas[j];
1875
- // Check if area's element is contained within otherArea's element
1876
- if (otherArea.element.contains(area.element)) {
1877
- // Find the closest parent (not just any ancestor)
1878
- if (!area.parentNode || area.parentNode.element.contains(otherArea.element)) {
1879
- // Remove from old parent if exists
1880
- if (area.parentNode) {
1881
- area.parentNode.childNodes.delete(area);
1882
- }
1883
- // Set new parent
1884
- area.parentNode = otherArea;
1885
- otherArea.childNodes.add(area);
1886
- }
1887
- }
1888
- }
1889
- }
1890
- }
1891
-
1892
- class Logger {
1893
- config = {
1894
- enabled: false,
1895
- prefix: '',
1896
- timestamp: false,
1897
- };
1898
- /**
1899
- * Cấu hình logger
1900
- * @param config - Cấu hình logger
1901
- */
1902
- configure(config) {
1903
- this.config = { ...this.config, ...config };
1904
- }
1905
- /**
1906
- * Lấy cấu hình hiện tại
1907
- */
1908
- getConfig() {
1909
- return { ...this.config };
1910
- }
1911
- /**
1912
- * Bật logger
1913
- */
1914
- enable() {
1915
- this.config.enabled = true;
1916
- }
1917
- /**
1918
- * Tắt logger
1919
- */
1920
- disable() {
1921
- this.config.enabled = false;
1661
+ /**
1662
+ * Tắt logger
1663
+ */
1664
+ disable() {
1665
+ this.config.enabled = false;
1922
1666
  }
1923
1667
  /**
1924
1668
  * Format message với prefix và timestamp
@@ -2026,7 +1770,7 @@ class Logger {
2026
1770
  }
2027
1771
  }
2028
1772
  // Export singleton instance
2029
- const logger$4 = new Logger();
1773
+ const logger$9 = new Logger();
2030
1774
  // Export factory function để tạo logger với config riêng
2031
1775
  function createLogger(config = {}) {
2032
1776
  const instance = new Logger();
@@ -2034,174 +1778,503 @@ function createLogger(config = {}) {
2034
1778
  return instance;
2035
1779
  }
2036
1780
 
2037
- function findLastSizeOfDom(data) {
2038
- const listDocs = data
2039
- .filter((item) => item.doc?.find((doc) => doc.data.width && doc.data.height))
2040
- .flatMap((item) => item.doc?.flatMap((doc) => doc.data));
2041
- const lastDoc = listDocs?.[listDocs.length - 1];
2042
- const docSize = {
2043
- width: lastDoc?.width,
2044
- height: lastDoc?.height,
2045
- };
2046
- const listResizes = data.filter((item) => !!item.resize).flatMap((item) => item.resize);
2047
- const lastResizeEvent = listResizes?.[listResizes.length - 1];
2048
- const resize = {
2049
- width: lastResizeEvent?.data.width,
2050
- height: lastResizeEvent?.data.height,
2051
- };
2052
- return {
2053
- doc: docSize,
2054
- resize: resize,
2055
- size: {
2056
- width: resize.width || docSize.width,
2057
- height: resize.height || docSize.height,
1781
+ /**
1782
+ * Create an observable value with subscribe/unsubscribe pattern
1783
+ */
1784
+ function createObservable(initialValue) {
1785
+ const subscribers = new Set();
1786
+ const observable = {
1787
+ value: initialValue,
1788
+ observe: (callback) => {
1789
+ subscribers.add(callback);
1790
+ // Immediately call with current value
1791
+ if (observable.value !== undefined) {
1792
+ callback(observable.value);
1793
+ }
1794
+ },
1795
+ unobserve: (callback) => {
1796
+ subscribers.delete(callback);
1797
+ },
1798
+ update: (newValue) => {
1799
+ observable.value = newValue;
1800
+ // Notify all subscribers
1801
+ subscribers.forEach((callback) => {
1802
+ callback(newValue);
1803
+ });
2058
1804
  },
2059
1805
  };
2060
- }
2061
- function decodeClarity(payload) {
2062
- try {
2063
- return decode(payload);
2064
- }
2065
- catch (_error) {
2066
- return null;
2067
- }
2068
- }
2069
- function decodeArrayClarity(items) {
2070
- return items.map((item) => decodeClarity(item)).filter((item) => item !== null);
1806
+ return observable;
2071
1807
  }
2072
1808
 
2073
- function findElementByHash(props) {
2074
- const { hash, selector, iframeDocument, vizRef } = props;
2075
- if (vizRef) {
2076
- const element = vizRef.get(hash);
2077
- return element;
2078
- }
2079
- // Fallback
2080
- if (!iframeDocument)
2081
- return null;
2082
- try {
2083
- const element = selector ? iframeDocument.querySelector(selector) : null;
2084
- if (element) {
2085
- return element;
2086
- }
2087
- }
2088
- catch (error) {
2089
- logger$4.warn(`Invalid selector "${selector}":`, error);
2090
- }
2091
- const elementByHash = iframeDocument.querySelector(`[data-clarity-hashalpha="${hash}"], [data-clarity-hash="${hash}"], [data-clarity-hashbeta="${hash}"]`);
2092
- return elementByHash;
1809
+ function sortEvents(a, b) {
1810
+ return a.time - b.time;
2093
1811
  }
2094
1812
 
2095
1813
  /**
2096
- * Hydrates persisted area data into full area node
2097
- * Finds element in DOM and calculates all runtime values
2098
- *
2099
- * @param persistedData - Minimal data from database
2100
- * @param iframeDocument - Document to find element in
2101
- * @param heatmapInfo - Heatmap data for click calculations
2102
- * @param vizRef - Map of hash to elements
2103
- * @param shadowRoot - Optional shadow root for rect calculation
2104
- * @returns Full area node or null if element not found
1814
+ * Throttle a function using requestAnimationFrame
1815
+ * Ensures the callback is called at most once per animation frame
2105
1816
  */
2106
- function hydrateAreaNode(props) {
2107
- const { persistedData, iframeDocument, heatmapInfo, vizRef, shadowRoot } = props;
2108
- const { id, hash, selector } = persistedData;
2109
- const element = findElementByHash({ hash, selector, iframeDocument, vizRef });
2110
- if (!element) {
2111
- logger$4.warn(`Cannot hydrate area ${id}: element not found for hash ${hash} or selector ${selector}`);
2112
- return null;
2113
- }
2114
- const areaNode = buildAreaNode(element, hash, heatmapInfo, shadowRoot, persistedData);
2115
- if (!areaNode)
2116
- return null;
2117
- return areaNode;
2118
- }
2119
- function hydrateAreas(props) {
2120
- const { clickAreas, iframeDocument, heatmapInfo, vizRef, shadowRoot } = props;
2121
- const hydratedAreas = [];
2122
- for (const persistedData of clickAreas) {
2123
- const area = hydrateAreaNode({ persistedData, iframeDocument, heatmapInfo, vizRef, shadowRoot });
2124
- if (area) {
2125
- hydratedAreas.push(area);
2126
- }
2127
- }
2128
- logger$4.info(`Hydrated ${hydratedAreas.length} of ${clickAreas.length} persisted areas`);
2129
- return hydratedAreas;
2130
- }
1817
+ const throttleRAF = (callback) => {
1818
+ let rafId = null;
1819
+ let latestArgs = null;
1820
+ const throttled = (...args) => {
1821
+ // Store the latest arguments
1822
+ latestArgs = args;
1823
+ // If already scheduled, do nothing
1824
+ if (rafId !== null)
1825
+ return;
1826
+ // Schedule the callback for the next animation frame
1827
+ rafId = requestAnimationFrame(() => {
1828
+ if (latestArgs !== null) {
1829
+ callback(...latestArgs);
1830
+ latestArgs = null;
1831
+ }
1832
+ rafId = null;
1833
+ });
1834
+ };
1835
+ // Add cancel method to clear pending RAF
1836
+ throttled.cancel = () => {
1837
+ if (rafId !== null) {
1838
+ cancelAnimationFrame(rafId);
1839
+ rafId = null;
1840
+ latestArgs = null;
1841
+ }
1842
+ };
1843
+ return throttled;
1844
+ };
1845
+
1846
+ const AREA_HOVER_BOX_SHADOW = '0 0 0 1px #0078D4, 0 0 0 1px #0078D4 inset, 0 0 0 2px white inset';
1847
+ const AREA_HOVER_ELEMENT_ID = 'clarity-edit-hover';
1848
+ const AREA_MAP_DIV_ATTRIBUTE = 'data-clarity-area-map-div';
1849
+ const HEATMAP_AREA_CONTAINER_CLASS = 'heatmap-area-container';
1850
+ const HEATMAP_AREA_CONTAINER_SELECTOR = `.${HEATMAP_AREA_CONTAINER_CLASS}`;
1851
+ const AREA_CONTAINER_STYLES = `
1852
+ position: absolute;
1853
+ top: 0;
1854
+ left: 0;
1855
+ width: 100%;
1856
+ height: 100%;
1857
+ pointer-events: none;
1858
+ z-index: 999999;
1859
+ `;
1860
+ const AREA_INNER_CONTAINER_STYLES = `
1861
+ position: relative;
1862
+ width: 100%;
1863
+ height: 100%;
1864
+ `;
1865
+ const AREA_COLOR_GRADIENT = [
1866
+ [0, 0, 255], // Blue
1867
+ [0, 255, 255], // Cyan
1868
+ [0, 255, 0], // Green
1869
+ [255, 255, 0], // Yellow
1870
+ [255, 0, 0], // Red
1871
+ ];
1872
+ const AREA_RENDERER_SELECTORS = {
1873
+ containerAttribute: AREA_MAP_DIV_ATTRIBUTE,
1874
+ containerSelector: `[${AREA_MAP_DIV_ATTRIBUTE}]`,
1875
+ innerContainerClass: HEATMAP_AREA_CONTAINER_CLASS,
1876
+ innerContainerSelector: HEATMAP_AREA_CONTAINER_SELECTOR,
1877
+ };
1878
+
1879
+ const CALLOUT_PADDING = 0;
1880
+ const CALLOUT_ARROW_SIZE = 8;
1881
+ const CALLOUT_OFFSET = { x: -8, y: 0 };
1882
+ const CALLOUT_ALIGNMENT = 'left';
1883
+ const CLICKED_ELEMENT_ID_BASE = 'gx-hm-clicked-element';
1884
+ const SECONDARY_CLICKED_ELEMENT_ID_BASE = 'gx-hm-secondary-clicked-element';
1885
+ const HOVERED_ELEMENT_ID_BASE = 'gx-hm-hovered-element';
1886
+ const SECONDARY_HOVERED_ELEMENT_ID_BASE = 'gx-hm-secondary-hovered-element';
1887
+ const DEFAULT_POSITION_MODE = 'absolute';
1888
+
2131
1889
  /**
2132
- * Serializes area node to persisted data for database storage
1890
+ * Get color from click distribution percentage (0-100)
2133
1891
  */
2134
- function serializeAreaNode(area) {
2135
- return {
2136
- kind: area.kind,
2137
- id: area.id,
2138
- hash: area.hash,
2139
- selector: area.selector,
2140
- };
1892
+ function getColorFromClickDist(clickDist) {
1893
+ // Ensure clickDist is in range [0, 100]
1894
+ const normalizedDist = Math.max(0, Math.min(100, clickDist));
1895
+ // Calculate gradient index
1896
+ const maxIndex = AREA_COLOR_GRADIENT.length - 1;
1897
+ const index = Math.floor((normalizedDist / 100) * maxIndex);
1898
+ const clampedIndex = Math.min(index, maxIndex);
1899
+ const [r, g, b] = AREA_COLOR_GRADIENT[clampedIndex];
1900
+ // Return rgba with 60% opacity
1901
+ return `rgba(${r}, ${g}, ${b}, 0.6)`;
2141
1902
  }
2142
1903
  /**
2143
- * Serializes multiple areas for database storage
1904
+ * Get hover color (slightly lighter) from click distribution
2144
1905
  */
2145
- function serializeAreas(areas) {
2146
- return areas.map(serializeAreaNode);
1906
+ function getHoverColorFromClickDist(clickDist) {
1907
+ const normalizedDist = Math.max(0, Math.min(100, clickDist));
1908
+ const maxIndex = AREA_COLOR_GRADIENT.length - 1;
1909
+ const index = Math.floor((normalizedDist / 100) * maxIndex);
1910
+ const clampedIndex = Math.min(index, maxIndex);
1911
+ const [r, g, b] = AREA_COLOR_GRADIENT[clampedIndex];
1912
+ // Return rgba with 80% opacity for hover
1913
+ return `rgba(${r}, ${g}, ${b}, 0.8)`;
2147
1914
  }
2148
-
2149
1915
  /**
2150
- * Resolve overlapping areas by priority rules
2151
- *
2152
- * Priority Rules (in order):
2153
- * 1. Priority flag (manually set areas win)
2154
- * 2. Click distribution (higher % wins)
2155
- * 3. Total clicks (more clicks wins)
2156
- * 4. DOM containment (parent contains child, parent wins)
2157
- * 5. Size (smaller areas win - more specific)
1916
+ * Calculate click distribution percentage from total clicks
2158
1917
  */
2159
- function resolveOverlaps(areas, iframeDocument) {
2160
- if (areas.length === 0)
2161
- return [];
2162
- // Group overlapping areas
2163
- const overlapGroups = findOverlapGroups(areas);
2164
- // Resolve each group
2165
- const visibleAreas = new Set();
2166
- overlapGroups.forEach((group) => {
2167
- const winner = resolveOverlapGroup(group, iframeDocument);
2168
- visibleAreas.add(winner);
2169
- });
2170
- // Add non-overlapping areas
2171
- areas.forEach((area) => {
2172
- const hasOverlap = overlapGroups.some((group) => group.areas.includes(area));
2173
- if (!hasOverlap) {
2174
- visibleAreas.add(area);
1918
+ function calculateClickDistribution(elementClicks, totalClicks) {
1919
+ if (totalClicks === 0)
1920
+ return 0;
1921
+ return (elementClicks / totalClicks) * 100;
1922
+ }
1923
+
1924
+ function getElementRect(element, _shadowRoot) {
1925
+ const rect = element.getBoundingClientRect();
1926
+ const width = rect.width;
1927
+ const height = rect.height;
1928
+ const doc = element.ownerDocument || document;
1929
+ const scrollTop = doc.documentElement?.scrollTop || doc.body?.scrollTop || 0;
1930
+ const scrollLeft = doc.documentElement?.scrollLeft || doc.body?.scrollLeft || 0;
1931
+ const top = rect.top + scrollTop;
1932
+ const left = rect.left + scrollLeft;
1933
+ const absoluteLeft = left;
1934
+ const absoluteTop = top;
1935
+ const absoluteRight = absoluteLeft + width;
1936
+ const absoluteBottom = absoluteTop + height;
1937
+ return {
1938
+ width,
1939
+ height,
1940
+ top,
1941
+ left,
1942
+ absoluteLeft,
1943
+ absoluteTop,
1944
+ absoluteRight,
1945
+ absoluteBottom,
1946
+ outOfBounds: false,
1947
+ };
1948
+ }
1949
+ function isElementFixed(element) {
1950
+ if (getComputedStyle(element).position === 'fixed') {
1951
+ return true;
1952
+ }
1953
+ if (element.nodeName === 'HTML') {
1954
+ return false;
1955
+ }
1956
+ const parent = element.parentElement;
1957
+ return parent ? isElementFixed(parent) : false;
1958
+ }
1959
+ function doAreasOverlap(area1, area2) {
1960
+ const r1 = area1.rect.value;
1961
+ const r2 = area2.rect.value;
1962
+ if (!r1 || !r2)
1963
+ return false;
1964
+ return ((r1.absoluteBottom > r2.absoluteTop &&
1965
+ r1.absoluteTop < r2.absoluteBottom &&
1966
+ r1.absoluteRight > r2.absoluteLeft &&
1967
+ r1.absoluteLeft < r2.absoluteRight) ||
1968
+ (r2.absoluteBottom > r1.absoluteTop &&
1969
+ r2.absoluteTop < r1.absoluteBottom &&
1970
+ r2.absoluteRight > r1.absoluteLeft &&
1971
+ r2.absoluteLeft < r1.absoluteRight));
1972
+ }
1973
+ function isAreaContainedIn(area1, area2) {
1974
+ const r1 = area1.rect.value;
1975
+ const r2 = area2.rect.value;
1976
+ if (!r1 || !r2)
1977
+ return false;
1978
+ return (r1.absoluteTop >= r2.absoluteTop &&
1979
+ r1.absoluteBottom <= r2.absoluteBottom &&
1980
+ r1.absoluteLeft >= r2.absoluteLeft &&
1981
+ r1.absoluteRight <= r2.absoluteRight);
1982
+ }
1983
+ function isElementAncestorOf(ancestor, descendant, doc) {
1984
+ return ancestor.contains(descendant);
1985
+ }
1986
+ function isElementSelectable(element, index, elements) {
1987
+ if (isIgnoredCanvas(element)) {
1988
+ return false;
1989
+ }
1990
+ if (element.hasAttribute(AREA_MAP_DIV_ATTRIBUTE)) {
1991
+ return false;
1992
+ }
1993
+ if (index === 0 && elements.length > 1 && element.nodeName === 'BODY') {
1994
+ return false;
1995
+ }
1996
+ return true;
1997
+ }
1998
+ function sortAreasByClickDist(areas) {
1999
+ return [...areas].sort((a, b) => {
2000
+ if (a.clickDist !== b.clickDist) {
2001
+ return b.clickDist - a.clickDist;
2175
2002
  }
2003
+ return b.totalclicks - a.totalclicks;
2176
2004
  });
2177
- return Array.from(visibleAreas);
2178
2005
  }
2179
- /**
2180
- * Find groups of overlapping areas
2181
- */
2182
- function findOverlapGroups(areas) {
2183
- const groups = [];
2184
- const processed = new Set();
2185
- areas.forEach((area) => {
2186
- if (processed.has(area.id))
2187
- return;
2188
- // Find all areas that overlap with this one
2189
- const overlapping = areas.filter((other) => other.id !== area.id && doAreasOverlap(area, other));
2190
- if (overlapping.length === 0) {
2191
- // No overlap, skip grouping
2192
- return;
2006
+ function isRectTooSmallForLabel(rect) {
2007
+ return rect.width < 67 || rect.height < 30;
2008
+ }
2009
+
2010
+ function getElementSelector(element) {
2011
+ if (element.id) {
2012
+ return `#${element.id}`;
2013
+ }
2014
+ if (element.className) {
2015
+ const classes = Array.from(element.classList).join('.');
2016
+ if (classes) {
2017
+ return `${element.tagName.toLowerCase()}.${classes}`;
2193
2018
  }
2194
- // Create group with this area and all overlapping
2195
- const groupAreas = [area, ...overlapping];
2196
- groupAreas.forEach((a) => processed.add(a.id));
2197
- // Placeholder - will be resolved later
2198
- groups.push({
2199
- areas: groupAreas,
2200
- winner: area,
2201
- hidden: [],
2202
- });
2203
- });
2204
- return groups;
2019
+ }
2020
+ return element.tagName.toLowerCase();
2021
+ }
2022
+ /**
2023
+ * Calculate total clicks for an element including all its child elements
2024
+ * @param element - The parent element
2025
+ * @param elementMapInfo - Map of hash to element click info
2026
+ * @returns Total clicks for element + all descendants
2027
+ */
2028
+ function calculateTotalClicksWithChildren(element, clickMapMetrics) {
2029
+ let totalClicks = 0;
2030
+ // Get clicks for the element itself
2031
+ const elementHash = getElementHash(element);
2032
+ if (elementHash) {
2033
+ const elementInfo = clickMapMetrics[elementHash];
2034
+ totalClicks += elementInfo?.totalClicked.value ?? 0;
2035
+ }
2036
+ const children = element.querySelectorAll('*');
2037
+ children.forEach((child) => {
2038
+ const childHash = getElementHash(child);
2039
+ if (childHash) {
2040
+ const childInfo = clickMapMetrics[childHash];
2041
+ totalClicks += childInfo?.totalClicked.value ?? 0;
2042
+ }
2043
+ });
2044
+ return totalClicks;
2045
+ }
2046
+ function buildAreaNode(element, hash, heatmapInfo, shadowRoot, persistedData) {
2047
+ if (!heatmapInfo.clickMapMetrics)
2048
+ return;
2049
+ const totalClicks = heatmapInfo.totalClicks || 0;
2050
+ const elementInfo = heatmapInfo.clickMapMetrics[hash];
2051
+ // Calculate total clicks including all child elements
2052
+ const elementClicks = calculateTotalClicksWithChildren(element, heatmapInfo.clickMapMetrics);
2053
+ const clickDist = calculateClickDistribution(elementClicks, totalClicks);
2054
+ const rect = getElementRect(element);
2055
+ const color = getColorFromClickDist(clickDist);
2056
+ const hoverColor = getHoverColorFromClickDist(clickDist);
2057
+ const areaNode = {
2058
+ kind: persistedData?.kind || 'area',
2059
+ id: persistedData?.id || `${hash}_${Date.now()}`,
2060
+ hash,
2061
+ selector: persistedData?.selector || elementInfo?.selector || getElementSelector(element),
2062
+ // DOM references
2063
+ element,
2064
+ areaElement: null,
2065
+ shadowElement: null,
2066
+ shadowStyleElement: null,
2067
+ // Graph structure
2068
+ parentNode: null,
2069
+ childNodes: new Set(),
2070
+ // Position
2071
+ rect: createObservable(rect),
2072
+ isFixed: isElementFixed(element),
2073
+ priority: false,
2074
+ // Click tracking
2075
+ totalclicks: elementClicks,
2076
+ cumulativeClicks: elementClicks,
2077
+ cumulativeMaxClicks: totalClicks,
2078
+ clickDist,
2079
+ hasClickInfo: true,
2080
+ // Visual
2081
+ color,
2082
+ hoverColor,
2083
+ // Lifecycle
2084
+ changeObserver: null,
2085
+ };
2086
+ return areaNode;
2087
+ }
2088
+ function getTopElementsByClicks(clickMapMetrics, topN = 10) {
2089
+ const elements = Object.entries(clickMapMetrics)
2090
+ .map(([hash, info]) => ({
2091
+ hash,
2092
+ totalclicks: info.totalClicked.value ?? 0,
2093
+ selector: info.selector || '',
2094
+ }))
2095
+ .sort((a, b) => b.totalclicks - a.totalclicks)
2096
+ .slice(0, topN);
2097
+ return elements;
2098
+ }
2099
+
2100
+ /**
2101
+ * Build parent-child relationships between areas based on DOM hierarchy
2102
+ * @param areas - Array of area nodes to build relationships for
2103
+ */
2104
+ function buildAreaGraph(areas) {
2105
+ // Clear existing relationships
2106
+ areas.forEach((area) => {
2107
+ area.parentNode = null;
2108
+ area.childNodes.clear();
2109
+ });
2110
+ // Build relationships based on DOM containment
2111
+ for (let i = 0; i < areas.length; i++) {
2112
+ const area = areas[i];
2113
+ for (let j = 0; j < areas.length; j++) {
2114
+ if (i === j)
2115
+ continue;
2116
+ const otherArea = areas[j];
2117
+ // Check if area's element is contained within otherArea's element
2118
+ if (otherArea.element.contains(area.element)) {
2119
+ // Find the closest parent (not just any ancestor)
2120
+ if (!area.parentNode || area.parentNode.element.contains(otherArea.element)) {
2121
+ // Remove from old parent if exists
2122
+ if (area.parentNode) {
2123
+ area.parentNode.childNodes.delete(area);
2124
+ }
2125
+ // Set new parent
2126
+ area.parentNode = otherArea;
2127
+ otherArea.childNodes.add(area);
2128
+ }
2129
+ }
2130
+ }
2131
+ }
2132
+ }
2133
+
2134
+ function decodeClarity(payload) {
2135
+ try {
2136
+ return decode(payload);
2137
+ }
2138
+ catch (_error) {
2139
+ return null;
2140
+ }
2141
+ }
2142
+ function decodeArrayClarity(items) {
2143
+ return items.map((item) => decodeClarity(item)).filter((item) => item !== null);
2144
+ }
2145
+
2146
+ function findElementByHash(props) {
2147
+ const { hash, selector, iframeDocument, vizRef } = props;
2148
+ if (vizRef) {
2149
+ const element = vizRef.get(hash);
2150
+ return element;
2151
+ }
2152
+ // Fallback
2153
+ if (!iframeDocument)
2154
+ return null;
2155
+ try {
2156
+ const element = selector ? iframeDocument.querySelector(selector) : null;
2157
+ if (element) {
2158
+ return element;
2159
+ }
2160
+ }
2161
+ catch (error) {
2162
+ logger$9.warn(`Invalid selector "${selector}":`, error);
2163
+ }
2164
+ const elementByHash = iframeDocument.querySelector(`[data-clarity-hashalpha="${hash}"], [data-clarity-hash="${hash}"], [data-clarity-hashbeta="${hash}"]`);
2165
+ return elementByHash;
2166
+ }
2167
+
2168
+ /**
2169
+ * Hydrates persisted area data into full area node
2170
+ * Finds element in DOM and calculates all runtime values
2171
+ *
2172
+ * @param persistedData - Minimal data from database
2173
+ * @param iframeDocument - Document to find element in
2174
+ * @param heatmapInfo - Heatmap data for click calculations
2175
+ * @param vizRef - Map of hash to elements
2176
+ * @param shadowRoot - Optional shadow root for rect calculation
2177
+ * @returns Full area node or null if element not found
2178
+ */
2179
+ function hydrateAreaNode(props) {
2180
+ const { persistedData, iframeDocument, heatmapInfo, vizRef, shadowRoot } = props;
2181
+ const { id, hash, selector } = persistedData;
2182
+ const element = findElementByHash({ hash, selector, iframeDocument, vizRef });
2183
+ if (!element) {
2184
+ logger$9.warn(`Cannot hydrate area ${id}: element not found for hash ${hash} or selector ${selector}`);
2185
+ return null;
2186
+ }
2187
+ const areaNode = buildAreaNode(element, hash, heatmapInfo, shadowRoot, persistedData);
2188
+ if (!areaNode)
2189
+ return null;
2190
+ return areaNode;
2191
+ }
2192
+ function hydrateAreas(props) {
2193
+ const { clickAreas, iframeDocument, heatmapInfo, vizRef, shadowRoot } = props;
2194
+ const hydratedAreas = [];
2195
+ for (const persistedData of clickAreas) {
2196
+ const area = hydrateAreaNode({ persistedData, iframeDocument, heatmapInfo, vizRef, shadowRoot });
2197
+ if (area) {
2198
+ hydratedAreas.push(area);
2199
+ }
2200
+ }
2201
+ logger$9.info(`Hydrated ${hydratedAreas.length} of ${clickAreas.length} persisted areas`);
2202
+ return hydratedAreas;
2203
+ }
2204
+ /**
2205
+ * Serializes area node to persisted data for database storage
2206
+ */
2207
+ function serializeAreaNode(area) {
2208
+ return {
2209
+ kind: area.kind,
2210
+ id: area.id,
2211
+ hash: area.hash,
2212
+ selector: area.selector,
2213
+ };
2214
+ }
2215
+ /**
2216
+ * Serializes multiple areas for database storage
2217
+ */
2218
+ function serializeAreas(areas) {
2219
+ return areas.map(serializeAreaNode);
2220
+ }
2221
+
2222
+ /**
2223
+ * Resolve overlapping areas by priority rules
2224
+ *
2225
+ * Priority Rules (in order):
2226
+ * 1. Priority flag (manually set areas win)
2227
+ * 2. Click distribution (higher % wins)
2228
+ * 3. Total clicks (more clicks wins)
2229
+ * 4. DOM containment (parent contains child, parent wins)
2230
+ * 5. Size (smaller areas win - more specific)
2231
+ */
2232
+ function resolveOverlaps(areas, iframeDocument) {
2233
+ if (areas.length === 0)
2234
+ return [];
2235
+ // Group overlapping areas
2236
+ const overlapGroups = findOverlapGroups(areas);
2237
+ // Resolve each group
2238
+ const visibleAreas = new Set();
2239
+ overlapGroups.forEach((group) => {
2240
+ const winner = resolveOverlapGroup(group, iframeDocument);
2241
+ visibleAreas.add(winner);
2242
+ });
2243
+ // Add non-overlapping areas
2244
+ areas.forEach((area) => {
2245
+ const hasOverlap = overlapGroups.some((group) => group.areas.includes(area));
2246
+ if (!hasOverlap) {
2247
+ visibleAreas.add(area);
2248
+ }
2249
+ });
2250
+ return Array.from(visibleAreas);
2251
+ }
2252
+ /**
2253
+ * Find groups of overlapping areas
2254
+ */
2255
+ function findOverlapGroups(areas) {
2256
+ const groups = [];
2257
+ const processed = new Set();
2258
+ areas.forEach((area) => {
2259
+ if (processed.has(area.id))
2260
+ return;
2261
+ // Find all areas that overlap with this one
2262
+ const overlapping = areas.filter((other) => other.id !== area.id && doAreasOverlap(area, other));
2263
+ if (overlapping.length === 0) {
2264
+ // No overlap, skip grouping
2265
+ return;
2266
+ }
2267
+ // Create group with this area and all overlapping
2268
+ const groupAreas = [area, ...overlapping];
2269
+ groupAreas.forEach((a) => processed.add(a.id));
2270
+ // Placeholder - will be resolved later
2271
+ groups.push({
2272
+ areas: groupAreas,
2273
+ winner: area,
2274
+ hidden: [],
2275
+ });
2276
+ });
2277
+ return groups;
2205
2278
  }
2206
2279
  /**
2207
2280
  * Resolve a single overlap group to find the winner
@@ -2677,1365 +2750,168 @@ const constrainToViewport = (candidate, options) => {
2677
2750
  const viewportTop = padding;
2678
2751
  const viewportRight = viewport.width - calloutRect.width - padding;
2679
2752
  const viewportBottom = viewport.height - calloutRect.height - padding;
2680
- const left = Math.max(viewportLeft, Math.min(leftPos, viewportRight));
2681
- const top = Math.max(viewportTop, Math.min(topPos, viewportBottom));
2682
- return { top, left };
2683
- };
2684
-
2685
- const getScrollOffset = (visualRef) => {
2686
- if (!visualRef?.current)
2687
- return;
2688
- return {
2689
- top: visualRef.current.scrollTop,
2690
- left: visualRef.current.scrollLeft,
2691
- };
2692
- };
2693
- /**
2694
- * Create adjusted container rect for absolute positioning
2695
- * - With scroll: represents visible area in container coordinates
2696
- * - Without scroll: represents full container in container coordinates
2697
- */
2698
- const createAdjustedContainerRect = (options) => {
2699
- const { containerElm, scale, isAbsolute, visualRef } = options;
2700
- const containerRect = containerElm.getBoundingClientRect();
2701
- const scrollOffset = getScrollOffset(visualRef);
2702
- // No scale = fixed positioning, use raw rect
2703
- if (!scale)
2704
- return containerRect;
2705
- const scaledWidth = containerRect.width / scale;
2706
- const scaledHeight = containerRect.height / scale;
2707
- // Absolute positioning with scroll offset
2708
- if (isAbsolute && scrollOffset) {
2709
- return {
2710
- ...containerRect,
2711
- top: scrollOffset.top,
2712
- left: scrollOffset.left,
2713
- right: scrollOffset.left + scaledWidth,
2714
- bottom: scrollOffset.top + scaledHeight,
2715
- width: scaledWidth,
2716
- height: scaledHeight,
2717
- };
2718
- }
2719
- // Absolute positioning without scroll
2720
- return {
2721
- ...containerRect,
2722
- top: 0,
2723
- left: 0,
2724
- right: scaledWidth,
2725
- width: scaledWidth,
2726
- bottom: scaledHeight,
2727
- height: scaledHeight,
2728
- };
2729
- };
2730
- const calcCalloutPosition = (options) => {
2731
- const { targetElm, calloutElm, setPosition, positionMode, widthScale, visualRef } = options;
2732
- const offset = options.offset ?? CALLOUT_OFFSET;
2733
- const alignment = options.alignment ?? CALLOUT_ALIGNMENT;
2734
- const padding = CALLOUT_PADDING;
2735
- const arrowSize = CALLOUT_ARROW_SIZE;
2736
- return () => {
2737
- const isAbsolute = positionMode === 'absolute';
2738
- const scale = isAbsolute ? widthScale : 1;
2739
- // Determine container element based on positioning mode
2740
- // - Absolute: portal container (parent of callout)
2741
- // - Fixed: visual container (scrollable area)
2742
- const containerElm = isAbsolute ? calloutElm.parentElement : visualRef?.current;
2743
- if (!containerElm)
2744
- return;
2745
- const viewport = getContainerViewport(containerElm);
2746
- const visualViewport = getVisualDomViewport(visualRef?.current, scale);
2747
- const rectDimensions = getElementDimensions({ targetElm, calloutElm, scale, containerElm });
2748
- const containerRect = createAdjustedContainerRect({ containerElm, scale, isAbsolute, visualRef });
2749
- const options = {
2750
- rectDimensions,
2751
- viewport,
2752
- visualViewport,
2753
- alignment,
2754
- offset,
2755
- padding,
2756
- arrowSize,
2757
- containerRect,
2758
- widthScale,
2759
- };
2760
- const candidates = generateAllCandidates(options);
2761
- const candidate = selectBestPosition(candidates);
2762
- // Constrain to viewport/container bounds
2763
- const constrainedCandidate = constrainToViewport(candidate, options);
2764
- // Final callout position
2765
- const finalPosition = {
2766
- top: constrainedCandidate.top,
2767
- left: constrainedCandidate.left,
2768
- placement: candidate.placement,
2769
- horizontalAlign: candidate.horizontalAlign,
2770
- };
2771
- setPosition(finalPosition);
2772
- };
2773
- };
2774
- const calcCalloutPositionAbsolute = (props) => {
2775
- const { widthScale, calloutElm, containerElm, element, setPosition } = props;
2776
- const mousePosition = element?.mousePosition;
2777
- if (!mousePosition)
2778
- return;
2779
- const padding = props.padding ?? CALLOUT_PADDING;
2780
- const arrowSize = props.arrowSize ?? CALLOUT_ARROW_SIZE;
2781
- const rawCalloutRect = calloutElm.getBoundingClientRect();
2782
- if (rawCalloutRect.width === 0 || rawCalloutRect.height === 0)
2783
- return;
2784
- const containerRect = containerElm.getBoundingClientRect();
2785
- const mouseX = mousePosition.x;
2786
- const mouseY = mousePosition.y;
2787
- const targetRect = {
2788
- top: mouseY,
2789
- left: mouseX,
2790
- right: mouseX + 1,
2791
- bottom: mouseY + 1,
2792
- width: 1,
2793
- height: 1,
2794
- x: mouseX,
2795
- y: mouseY,
2796
- toJSON: () => ({}),
2797
- };
2798
- const rectDimensions = {
2799
- targetRect,
2800
- calloutRect: getScaledCalloutRect(),
2801
- targetAbsoluteRect: {
2802
- top: element.top,
2803
- left: element.left,
2804
- },
2805
- };
2806
- const viewport = getContainerViewport(containerElm);
2807
- const options = {
2808
- rectDimensions,
2809
- viewport,
2810
- alignment: CALLOUT_ALIGNMENT,
2811
- offset: CALLOUT_OFFSET,
2812
- padding,
2813
- arrowSize,
2814
- containerRect,
2815
- widthScale,
2816
- };
2817
- const candidates = generateAllCandidates(options);
2818
- const bestPosition = selectBestPosition(candidates);
2819
- const finalPosition = {
2820
- top: bestPosition.top,
2821
- left: bestPosition.left,
2822
- placement: bestPosition.placement,
2823
- horizontalAlign: bestPosition.horizontalAlign,
2824
- };
2825
- setPosition(finalPosition);
2826
- // const styleBestPosition = getStyleFromCandidate(bestPosition, widthScale);
2827
- // onChange(styleBestPosition);
2828
- };
2829
-
2830
- /**
2831
- * Throttle a function using requestAnimationFrame
2832
- * Ensures the callback is called at most once per animation frame
2833
- */
2834
- const throttleRAF = (callback) => {
2835
- let rafId = null;
2836
- let latestArgs = null;
2837
- const throttled = (...args) => {
2838
- // Store the latest arguments
2839
- latestArgs = args;
2840
- // If already scheduled, do nothing
2841
- if (rafId !== null)
2842
- return;
2843
- // Schedule the callback for the next animation frame
2844
- rafId = requestAnimationFrame(() => {
2845
- if (latestArgs !== null) {
2846
- callback(...latestArgs);
2847
- latestArgs = null;
2848
- }
2849
- rafId = null;
2850
- });
2851
- };
2852
- // Add cancel method to clear pending RAF
2853
- throttled.cancel = () => {
2854
- if (rafId !== null) {
2855
- cancelAnimationFrame(rafId);
2856
- rafId = null;
2857
- latestArgs = null;
2858
- }
2859
- };
2860
- return throttled;
2861
- };
2862
-
2863
- /**
2864
- * IframeHeightProcessor Types
2865
- * Complete TypeScript types for iframe height processing system
2866
- */
2867
- const TAGS_TO_PROCESS = [
2868
- 'div',
2869
- 'section',
2870
- 'iframe',
2871
- 'img',
2872
- 'a',
2873
- 'header',
2874
- 'main',
2875
- 'h1',
2876
- 'h2',
2877
- 'h3',
2878
- 'h4',
2879
- 'h5',
2880
- 'button',
2881
- 'ul',
2882
- 'svg',
2883
- 'li',
2884
- 'cart-drawer',
2885
- 'footer',
2886
- 'collapsible-content',
2887
- 'section-animate',
2888
- 'video',
2889
- ];
2890
- const ANIMATION_CLASSES_TO_REMOVE = [
2891
- 'Image--zoomOut',
2892
- 'Image--fadeIn',
2893
- 'transition--blur-up',
2894
- 'header-fixed',
2895
- 'lazyframe',
2896
- 'lazyframe--loaded',
2897
- 'lazymap',
2898
- 'pplr',
2899
- 'site-header--stuck',
2900
- 'animate--slide-in',
2901
- 'animated-element',
2902
- 'lazyPicture__elem',
2903
- 'jdgm-hidden',
2904
- 'splide',
2905
- 'list-collections__slide-overlay',
2906
- 'onetrust-pc-dark-filter',
2907
- 'featured-blog__header-image',
2908
- 'overlay', // Special case: only remove if NOT js-search-overlay
2909
- ];
2910
- const LAZY_LOAD_CLASSES_TO_REMOVE = [
2911
- 'lazyloaded',
2912
- 'lazyautosizes',
2913
- 'autosizes',
2914
- 'lazyload',
2915
- 'Image--fadeIn',
2916
- 'Image--lazyLoaded',
2917
- 'Image--lazyLoad',
2918
- 'lazypreload',
2919
- 'transition--fade-in',
2920
- ];
2921
- const ELEMENTS_TO_SKIP_OPACITY = [
2922
- 'header-menu',
2923
- 'header-megamenu',
2924
- 'header-dropdown',
2925
- 'image-overlay',
2926
- 'product-item__bg__under',
2927
- 'transition-opacity',
2928
- 'mm-drawer-cart-filter',
2929
- 'transition',
2930
- 'navigation__container',
2931
- 'predictive__content-mobile',
2932
- 'multiply-mode__target',
2933
- 'product-card__img',
2934
- 'feature__description',
2935
- 'slider-item',
2936
- 'rtnu-header__mobile-nav',
2937
- 'splide__slide',
2938
- 'sub-menu',
2939
- 'swiper-slide',
2940
- 'dropdown-menu',
2941
- 'header__mask',
2942
- 'announcement_block',
2943
- 'bottom_sticky',
2944
- 'stickyatc',
2945
- 'g-header__menu',
2946
- 'mobile-menu',
2947
- 'jc-absolute',
2948
- ];
2949
-
2950
- /**
2951
- * IframeHeightProcessor Utils
2952
- * Helper functions for iframe height processing
2953
- */
2954
- /**
2955
- * Get iframe element by ID
2956
- */
2957
- function getIframeElement(iframeId) {
2958
- return document.getElementById(iframeId);
2959
- }
2960
- /**
2961
- * Get iframe content document
2962
- */
2963
- function getIframeDocument(iframe) {
2964
- try {
2965
- return iframe.contentDocument || iframe.contentWindow?.document || null;
2966
- }
2967
- catch (error) {
2968
- console.warn('Cannot access iframe document:', error);
2969
- return null;
2970
- }
2971
- }
2972
- /**
2973
- * Get iframe content window
2974
- */
2975
- function getIframeWindow(iframe) {
2976
- try {
2977
- return iframe.contentWindow;
2978
- }
2979
- catch (error) {
2980
- console.warn('Cannot access iframe window:', error);
2981
- return null;
2982
- }
2983
- }
2984
- /**
2985
- * Calculate iframe height from multiple sources
2986
- * Returns the maximum height from body and documentElement
2987
- */
2988
- function calculateIframeHeight(doc) {
2989
- const body = doc.body;
2990
- const docElement = doc.documentElement;
2991
- const bodyOffsetHeight = body ? body.offsetHeight : 0;
2992
- const bodyScrollHeight = body ? body.scrollHeight : 0;
2993
- const documentElementOffsetHeight = docElement ? docElement.offsetHeight : 0;
2994
- const documentElementClientHeight = docElement ? docElement.clientHeight : 0;
2995
- const documentElementScrollHeight = docElement ? docElement.scrollHeight : 0;
2996
- const calculatedHeight = Math.max(bodyOffsetHeight, bodyScrollHeight, documentElementOffsetHeight, documentElementClientHeight, documentElementScrollHeight);
2997
- return {
2998
- bodyOffsetHeight,
2999
- bodyScrollHeight,
3000
- documentElementOffsetHeight,
3001
- documentElementClientHeight,
3002
- documentElementScrollHeight,
3003
- calculatedHeight,
3004
- };
3005
- }
3006
- /**
3007
- * Calculate iframe width
3008
- */
3009
- function calculateIframeWidth(doc) {
3010
- const body = doc.body;
3011
- const docElement = doc.documentElement;
3012
- return Math.max(body ? body.offsetWidth : 0, body ? body.scrollWidth : 0, docElement ? docElement.offsetWidth : 0, docElement ? docElement.clientWidth : 0, docElement ? docElement.scrollWidth : 0);
3013
- }
3014
- /**
3015
- * Check if element is a fixed drawer
3016
- */
3017
- function isFixedDrawer(element) {
3018
- if (!element.classList.contains('drawer__content')) {
3019
- return false;
3020
- }
3021
- const style = window.getComputedStyle(element);
3022
- return (style.getPropertyValue('position') === 'fixed' &&
3023
- style.getPropertyValue('top') === '0px' &&
3024
- style.getPropertyValue('left') === '0px');
3025
- }
3026
- /**
3027
- * Check if element is an image element (img, background-image, or data-bgset)
3028
- */
3029
- function isImageElement(element) {
3030
- if (element.tagName.toLowerCase() === 'img') {
3031
- return true;
3032
- }
3033
- const computedStyle = window.getComputedStyle(element);
3034
- const bgImage = computedStyle.getPropertyValue('background-image');
3035
- if (bgImage && /url\(.+?\)/.test(bgImage)) {
3036
- return true;
3037
- }
3038
- if (element.getAttribute('data-bgset')) {
3039
- return true;
3040
- }
3041
- return false;
3042
- }
3043
- /**
3044
- * Convert height to viewport units if applicable
3045
- */
3046
- function convertToViewportHeight(heightPx, viewportHeight, threshold = 60) {
3047
- if (heightPx >= viewportHeight) {
3048
- return '100vh';
3049
- }
3050
- const percentage = (heightPx / viewportHeight) * 100;
3051
- if (percentage > threshold) {
3052
- return `${Math.round(percentage)}vh`;
3053
- }
3054
- return null;
3055
- }
3056
- /**
3057
- * Parse srcset string and get appropriate image URL based on width
3058
- */
3059
- function getImageURLFromSrcSet(srcset, targetWidth) {
3060
- if (srcset.startsWith('data:image')) {
3061
- return '';
3062
- }
3063
- const sources = srcset.split(',').filter((s) => !!s);
3064
- for (const source of sources) {
3065
- const parts = source
3066
- .trim()
3067
- .split(' ')
3068
- .filter((p) => !!p);
3069
- const url = parts[0];
3070
- const descriptor = parts[1];
3071
- if (descriptor) {
3072
- const width = parseInt(descriptor.slice(0, -1), 10);
3073
- if (targetWidth <= width) {
3074
- return url;
3075
- }
3076
- }
3077
- }
3078
- // Return last source as fallback
3079
- const lastSource = sources[sources.length - 1].trim();
3080
- const parts = lastSource.split(' ');
3081
- if (parts.length > 3) {
3082
- throw new Error('Invalid src URL in srcset');
3083
- }
3084
- return parts[0];
3085
- }
3086
- /**
3087
- * Get background image URL from data-bgset attribute
3088
- */
3089
- function getBackgroundImageFromBgSet(bgset, targetWidth) {
3090
- const sources = bgset.split(',');
3091
- for (const source of sources) {
3092
- const parts = source.trim().split(' ');
3093
- if (parts.length >= 3) {
3094
- const url = parts[0];
3095
- const width = parseInt(parts[1], 10);
3096
- if (targetWidth < width) {
3097
- return url;
3098
- }
3099
- }
3100
- }
3101
- return null;
3102
- }
3103
- /**
3104
- * Replace placeholder in image URL (e.g., {width}, {height}, %7Bwidth%7D)
3105
- */
3106
- function replaceImagePlaceholder(url, placeholder, value) {
3107
- const patterns = [
3108
- new RegExp(`\\{${placeholder}\\}`, 'g'),
3109
- new RegExp(`%7B${placeholder}%7D`, 'gi'),
3110
- ];
3111
- let result = url;
3112
- for (const pattern of patterns) {
3113
- result = result.replace(pattern, String(value));
3114
- }
3115
- return result;
3116
- }
3117
- /**
3118
- * Get computed height attributes object
3119
- */
3120
- function getComputedHeightAttributes(element, computedStyle, windowHeight, windowWidth) {
3121
- return {
3122
- actualHeight: computedStyle.height,
3123
- actualWidth: computedStyle.width,
3124
- actualMaxHeight: computedStyle.maxHeight,
3125
- actualMaxWidth: computedStyle.maxWidth,
3126
- actualMinHeight: computedStyle.minHeight,
3127
- actualMinWidth: computedStyle.minWidth,
3128
- windowHeight: String(windowHeight),
3129
- windowWidth: String(windowWidth),
3130
- elementScrollHeight: String(element.scrollHeight),
3131
- };
3132
- }
3133
- /**
3134
- * Set computed height attributes on element
3135
- */
3136
- function setComputedHeightAttributes(element, attributes) {
3137
- Object.entries(attributes).forEach(([key, value]) => {
3138
- if (value !== undefined) {
3139
- element.setAttribute(key, value);
3140
- }
3141
- });
3142
- }
3143
- /**
3144
- * Remove all height-related attributes
3145
- */
3146
- function removeHeightAttributes(element) {
3147
- const attrs = [
3148
- 'actualHeight',
3149
- 'actualWidth',
3150
- 'actualMaxHeight',
3151
- 'actualMaxWidth',
3152
- 'actualMinHeight',
3153
- 'actualMinWidth',
3154
- 'windowHeight',
3155
- 'windowWidth',
3156
- 'elementScrollHeight',
3157
- 'viewportHeight',
3158
- 'viewportMinHeight',
3159
- 'viewportWidth',
3160
- ];
3161
- attrs.forEach((attr) => element.removeAttribute(attr));
3162
- }
3163
- /**
3164
- * Log with optional debug flag
3165
- */
3166
- function log(message, data, debug = false) {
3167
- if (debug || (typeof window !== 'undefined' && window.debugMode)) {
3168
- if (data !== undefined) {
3169
- console.log(`[IframeHeightProcessor] ${message}`, data);
3170
- }
3171
- else {
3172
- console.log(`[IframeHeightProcessor] ${message}`);
3173
- }
3174
- }
3175
- }
3176
- /**
3177
- * Check if element should skip opacity setting
3178
- */
3179
- function shouldSkipOpacitySetting(element, classList) {
3180
- const rect = element.getBoundingClientRect();
3181
- // Skip if element is in top 300px
3182
- if (rect.top < 300) {
3183
- return true;
3184
- }
3185
- // Skip empty divs
3186
- if (element.tagName.toLowerCase() === 'div' && element.childElementCount === 0) {
3187
- return true;
3188
- }
3189
- // Skip if has specific classes
3190
- return classList.some((cls) => element.classList.contains(cls));
3191
- }
3192
-
3193
- /**
3194
- * IframeHeightProcessor
3195
- * Core class for processing iframe heights, capturing computed styles, and fixing layout issues
3196
- */
3197
- class IframeHeightProcessor {
3198
- config;
3199
- iframe = null;
3200
- iframeDoc = null;
3201
- iframeWindow = null;
3202
- initialStyles = [];
3203
- lastKnownHeight = 0;
3204
- constructor(config) {
3205
- this.config = {
3206
- defaultHeight: 800,
3207
- debug: false,
3208
- selectedScreenshotData: null,
3209
- isCompatibleWithInteractiveMode: true,
3210
- isRunningInInteractiveMode: false,
3211
- ...config,
3212
- };
3213
- this.initialize();
3214
- }
3215
- /**
3216
- * Initialize processor - get iframe references
3217
- */
3218
- initialize() {
3219
- this.iframe = getIframeElement(this.config.iframeId);
3220
- if (!this.iframe) {
3221
- throw new Error(`Iframe with ID "${this.config.iframeId}" not found`);
3222
- }
3223
- this.iframeDoc = getIframeDocument(this.iframe);
3224
- this.iframeWindow = getIframeWindow(this.iframe);
3225
- if (!this.iframeDoc || !this.iframeWindow) {
3226
- throw new Error('Cannot access iframe document or window');
3227
- }
3228
- log('Initialized', { iframeId: this.config.iframeId }, this.config.debug);
3229
- }
3230
- /**
3231
- * Get current iframe height
3232
- */
3233
- getIframeHeight() {
3234
- if (!this.iframeDoc) {
3235
- return 0;
3236
- }
3237
- const metrics = calculateIframeHeight(this.iframeDoc);
3238
- return metrics.calculatedHeight;
3239
- }
3240
- /**
3241
- * Get current iframe width
3242
- */
3243
- getIframeWidth() {
3244
- if (!this.iframeDoc) {
3245
- return this.config.iframeWidth;
3246
- }
3247
- return calculateIframeWidth(this.iframeDoc);
3248
- }
3249
- /**
3250
- * Get iframe document height (alternative method)
3251
- */
3252
- getIframeDocumentHeight() {
3253
- if (!this.iframe)
3254
- return 0;
3255
- const doc = this.iframe.contentWindow?.document;
3256
- if (!doc)
3257
- return 0;
3258
- const { body, documentElement } = doc;
3259
- const heights = [
3260
- body?.scrollHeight ?? 0,
3261
- body?.offsetHeight ?? 0,
3262
- documentElement?.scrollHeight ?? 0,
3263
- documentElement?.offsetHeight ?? 0,
3264
- documentElement?.clientHeight ?? 0,
3265
- ];
3266
- return Math.max(...heights, 0);
3267
- }
3268
- /**
3269
- * MAIN PIPELINE: Process iframe height
3270
- * This is the main entry point that runs the full pipeline
3271
- */
3272
- async processIframeHeight() {
3273
- log('Starting iframe height processing pipeline...', null, this.config.debug);
3274
- try {
3275
- // Step 1: Capture computed heights
3276
- this.captureComputedHeight();
3277
- // Step 2: Clean up DOM
3278
- this.cleanUpDOM();
3279
- // Step 3: Fix heights
3280
- this.fixHeights();
3281
- // Step 4: Screenshot fixes (if height > default)
3282
- const currentHeight = this.getIframeHeight();
3283
- if (currentHeight > this.config.defaultHeight) {
3284
- this.hideElementsIfLargeModal();
3285
- }
3286
- this.findScrollableElements();
3287
- this.correctImageSrcs();
3288
- this.correctHeightForCertainClasses();
3289
- // Step 5: Track height
3290
- this.lastKnownHeight = this.getIframeHeight();
3291
- log('Iframe height processing completed', { height: this.lastKnownHeight }, this.config.debug);
3292
- }
3293
- catch (error) {
3294
- console.error('[IframeHeightProcessor] Error processing iframe height:', error);
3295
- throw error;
3296
- }
3297
- }
3298
- /**
3299
- * STEP 1: Capture Computed Heights
3300
- * Saves all computed styles to element attributes
3301
- */
3302
- captureComputedHeight() {
3303
- if (!this.iframeDoc)
3304
- return;
3305
- log('Capturing computed heights...', null, this.config.debug);
3306
- const body = this.iframeDoc.body;
3307
- // Remove hidden elements
3308
- body.querySelectorAll('.heatmap-com__hidden-element').forEach((el) => {
3309
- el.classList.remove('heatmap-com__hidden-element');
3310
- });
3311
- // Hide GDPR overlay
3312
- const gdprOverlay = body.querySelector('#gdpr-blocking-page-overlay');
3313
- if (gdprOverlay) {
3314
- gdprOverlay.style.display = 'none';
3315
- }
3316
- // Hide popup forms (if not in screenshot mode)
3317
- if (!this.config.selectedScreenshotData) {
3318
- const popups = body.querySelectorAll('[aria-label="POPUP Form"], [data-section-id="popup"]');
3319
- popups.forEach((popup) => {
3320
- const parent = popup.parentElement;
3321
- if (parent) {
3322
- parent.style.setProperty('display', 'none', 'important');
3323
- }
3324
- });
3325
- }
3326
- // Remove media-transparent class
3327
- body.querySelectorAll('.media--transparent').forEach((el) => {
3328
- el.classList.remove('media--transparent');
3329
- });
3330
- // Process each tag type
3331
- TAGS_TO_PROCESS.forEach((tag) => {
3332
- this.captureComputedHeightForTag(tag);
3333
- });
3334
- // Special fixes
3335
- this.applySwiperSlideWidthFix();
3336
- this.applyCustomBlocksFix();
3337
- this.applyParallaxContainerFix();
3338
- this.applyHeaderPositionFix();
3339
- log('Computed heights captured', null, this.config.debug);
3340
- }
3341
- /**
3342
- * Capture computed height for a specific tag
3343
- */
3344
- captureComputedHeightForTag(tagName) {
3345
- if (!this.iframeDoc)
3346
- return;
3347
- const elements = this.iframeDoc.querySelectorAll(tagName);
3348
- const viewportHeight = this.config.defaultHeight;
3349
- elements.forEach((element) => {
3350
- const el = element;
3351
- const computedStyle = window.getComputedStyle(el);
3352
- // Force important padding if > 10px
3353
- const paddingTop = parseInt(computedStyle.getPropertyValue('padding-top'), 10) || 0;
3354
- const paddingBottom = parseInt(computedStyle.getPropertyValue('padding-bottom'), 10) || 0;
3355
- if (paddingTop > 10) {
3356
- el.style.setProperty('padding-top', `${paddingTop}px`, 'important');
3357
- }
3358
- if (paddingBottom > 10) {
3359
- el.style.setProperty('padding-bottom', `${paddingBottom}px`, 'important');
3360
- }
3361
- // Remove old attributes
3362
- removeHeightAttributes(el);
3363
- // Get computed values
3364
- const attrs = getComputedHeightAttributes(el, computedStyle, viewportHeight, this.config.iframeWidth);
3365
- // Convert heights to viewport units if applicable
3366
- const heightPx = parseFloat(computedStyle.height);
3367
- if (!isNaN(heightPx)) {
3368
- const vhHeight = convertToViewportHeight(heightPx, viewportHeight);
3369
- if (vhHeight) {
3370
- attrs.viewportHeight = vhHeight;
3371
- }
3372
- }
3373
- const minHeightPx = parseFloat(computedStyle.minHeight);
3374
- if (!isNaN(minHeightPx)) {
3375
- const vhMinHeight = convertToViewportHeight(minHeightPx, viewportHeight);
3376
- if (vhMinHeight) {
3377
- attrs.viewportMinHeight = vhMinHeight;
3378
- }
3379
- }
3380
- const widthPx = parseInt(computedStyle.width, 10);
3381
- if (widthPx === this.config.iframeWidth) {
3382
- attrs.viewportWidth = '100vw';
3383
- }
3384
- // Set all attributes
3385
- setComputedHeightAttributes(el, attrs);
3386
- // SVG special handling
3387
- if (tagName.toLowerCase() === 'svg') {
3388
- if (el.classList.contains('decor-svg') || el.parentElement?.classList.contains('decor')) {
3389
- el.style.setProperty('width', `${computedStyle.width}`, 'important');
3390
- el.style.setProperty('height', `${computedStyle.height}`, 'important');
3391
- el.style.setProperty('transform', 'none', 'important');
3392
- }
3393
- }
3394
- });
3395
- }
3396
- /**
3397
- * STEP 2: Clean Up DOM
3398
- */
3399
- cleanUpDOM() {
3400
- log('Cleaning up DOM...', null, this.config.debug);
3401
- this.fixImages();
3402
- }
3403
- /**
3404
- * Fix lazy-loaded images
3405
- */
3406
- fixImages() {
3407
- if (!this.iframeDoc)
3408
- return;
3409
- const images = this.iframeDoc.querySelectorAll('img');
3410
- const iframeContentDoc = this.iframeDoc;
3411
- images.forEach((img) => {
3412
- // Fix srcset from data attributes
3413
- const srcset = img.getAttribute('srcset');
3414
- const dataSrcset = img.getAttribute('data-srcset');
3415
- const dataLazySrcset = img.getAttribute('data-lazy-srcset');
3416
- const dataLazySrc = img.getAttribute('data-lazy-src');
3417
- if ((srcset && srcset.startsWith('data:image') && dataSrcset) || (!srcset && dataSrcset)) {
3418
- img.setAttribute('srcset', dataSrcset);
3419
- }
3420
- else if (!srcset && dataLazySrcset) {
3421
- img.setAttribute('srcset', dataLazySrcset);
3422
- }
3423
- else if (!srcset && dataLazySrc) {
3424
- img.setAttribute('srcset', dataLazySrc);
3425
- }
3426
- // Set opacity
3427
- const hasBlurTransition = img.classList.contains('transition--blur-up');
3428
- if (img.parentNode === iframeContentDoc.body) {
3429
- img.style.display = 'none';
3430
- }
3431
- else {
3432
- img.style.opacity = '1';
3433
- if (hasBlurTransition) {
3434
- img.style.setProperty('display', 'block', 'important');
3435
- }
3436
- }
3437
- // Get best image URL
3438
- if (img.currentSrc || img.getAttribute('data-src')) {
3439
- let bestSrc = img.currentSrc;
3440
- if (img.getAttribute('data-src')) {
3441
- bestSrc = img.getAttribute('data-src');
3442
- }
3443
- const imgSrcset = img.getAttribute('srcset');
3444
- if (imgSrcset) {
3445
- try {
3446
- const urlFromSrcset = getImageURLFromSrcSet(imgSrcset, this.config.iframeWidth);
3447
- bestSrc = urlFromSrcset.replace(/.*https:/, 'https:') || bestSrc;
3448
- }
3449
- catch {
3450
- // Use current src as fallback
3451
- }
3452
- }
3453
- // Remove lazy loading classes
3454
- LAZY_LOAD_CLASSES_TO_REMOVE.forEach((cls) => {
3455
- img.classList.remove(cls);
3456
- });
3457
- // Set src
3458
- img.setAttribute('src', bestSrc);
3459
- }
3460
- });
3461
- }
3462
- /**
3463
- * STEP 3: Fix Heights
3464
- * Apply height fixes for all elements
3465
- */
3466
- fixHeights() {
3467
- log('Fixing heights...', null, this.config.debug);
3468
- const result = {
3469
- processed: 0,
3470
- fixed: 0,
3471
- errors: 0,
3472
- skipped: 0,
3473
- };
3474
- TAGS_TO_PROCESS.forEach((tag) => {
3475
- const tagResult = this.fixActualHeight(tag);
3476
- result.processed += tagResult.processed;
3477
- result.fixed += tagResult.fixed;
3478
- result.errors += tagResult.errors;
3479
- result.skipped += tagResult.skipped;
3480
- });
3481
- log('Heights fixed', result, this.config.debug);
3482
- return result;
3483
- }
3484
- /**
3485
- * Fix actual height for specific tag
3486
- */
3487
- fixActualHeight(tagName) {
3488
- if (!this.iframeDoc) {
3489
- return { processed: 0, fixed: 0, errors: 0, skipped: 0 };
3490
- }
3491
- const result = { processed: 0, fixed: 0, errors: 0, skipped: 0 };
3492
- const elements = this.iframeDoc.querySelectorAll(tagName);
3493
- elements.forEach((element) => {
3494
- result.processed++;
3495
- try {
3496
- const el = element;
3497
- const computedStyle = window.getComputedStyle(el);
3498
- // Apply all element fixes
3499
- this.fixElementOpacity(el, computedStyle);
3500
- this.removeZeroHeightAttribute(el);
3501
- this.fixFlexBasis(el, computedStyle);
3502
- this.fixHiddenProductItem(el, computedStyle);
3503
- this.removeAnimationClasses(el);
3504
- this.fixImageOrBackground(el, tagName);
3505
- this.fixAnimateOnScroll(el);
3506
- this.fixViewportWidth(el);
3507
- // HEIGHT FIXING LOGIC
3508
- this.applyHeightFix(el);
3509
- result.fixed++;
3510
- }
3511
- catch (error) {
3512
- result.errors++;
3513
- console.error(`Error fixing height for ${tagName}:`, error);
3514
- }
3515
- });
3516
- return result;
3517
- }
3518
- /**
3519
- * Set opacity = 1 if element is visible and not in skip list
3520
- */
3521
- fixElementOpacity(el, computedStyle) {
3522
- if (computedStyle.display !== 'none' && !shouldSkipOpacitySetting(el, ELEMENTS_TO_SKIP_OPACITY)) {
3523
- el.style.opacity = '1';
3524
- }
3525
- }
3526
- /**
3527
- * Remove actualHeight='0px' attribute
3528
- */
3529
- removeZeroHeightAttribute(el) {
3530
- if (el.getAttribute('actualHeight') === '0px') {
3531
- el.removeAttribute('actualHeight');
3532
- }
3533
- }
3534
- /**
3535
- * Fix flex-basis: 100% by converting to auto + width: 100%
3536
- */
3537
- fixFlexBasis(el, computedStyle) {
3538
- if (computedStyle.getPropertyValue('flex-basis') === '100%') {
3539
- el.style.setProperty('flex-basis', 'auto');
3540
- el.style.setProperty('width', '100%');
3541
- }
3542
- }
3543
- /**
3544
- * Fix ProductItem elements with opacity=1 but visibility=hidden
3545
- */
3546
- fixHiddenProductItem(el, computedStyle) {
3547
- if (computedStyle.opacity === '1' &&
3548
- computedStyle.visibility === 'hidden' &&
3549
- el.classList.toString().includes('ProductItem')) {
3550
- el.style.visibility = 'visible';
3551
- }
3552
- }
3553
- /**
3554
- * Remove animation and lazy-load classes that prevent rendering
3555
- */
3556
- removeAnimationClasses(el) {
3557
- ANIMATION_CLASSES_TO_REMOVE.forEach((cls) => {
3558
- if (el.classList.contains(cls)) {
3559
- // Skip overlay with js-search-overlay
3560
- if (cls === 'overlay' && el.classList.contains('js-search-overlay')) {
3561
- return;
3562
- }
3563
- el.classList.remove(cls);
3564
- }
3565
- });
3566
- }
3567
- /**
3568
- * Fix images or background images based on tag type
3569
- */
3570
- fixImageOrBackground(el, tagName) {
3571
- if (tagName.toLowerCase() === 'img') {
3572
- this.fixImagePlaceholders(el);
3573
- }
3574
- else {
3575
- this.setBackgroundImage(el);
3576
- }
3577
- }
3578
- /**
3579
- * Fix AOS (Animate On Scroll) elements - force visibility
3580
- */
3581
- fixAnimateOnScroll(el) {
3582
- if (el.getAttribute('data-aos')) {
3583
- el.style.opacity = '1';
3584
- el.style.visibility = 'visible';
3585
- }
3586
- }
3587
- /**
3588
- * Fix viewport width elements (100vw → 100%)
3589
- */
3590
- fixViewportWidth(el) {
3591
- if (el.getAttribute('viewportWidth') === '100vw') {
3592
- el.style.width = '100%';
3593
- }
3594
- }
3595
- /**
3596
- * Apply height fix logic to element
3597
- * ⚠️ CRITICAL: This logic must match line 13258-13306 exactly
3598
- */
3599
- applyHeightFix(el) {
3600
- // Check if element is fixed drawer (skip height fixes)
3601
- if (isFixedDrawer(el)) {
3602
- el.setAttribute('heightReset', 'true');
3603
- return;
3604
- }
3605
- const actualHeight = el.getAttribute('actualHeight');
3606
- const hasStyleAttr = el.getAttribute('style');
3607
- const isImage = isImageElement(el);
3608
- const actualHeightNum = parseInt(actualHeight || '0', 10);
3609
- // ===== START OF NESTED IF-ELSE CHAIN (line 13259-13306) =====
3610
- // Case 1: actualHeight = '100%'
3611
- if (actualHeight === '100%') {
3612
- console.log(`🚀 🐥 ~ IframeHeightProcessor ~ applyHeightFix ~ actualHeight:`, actualHeight);
3613
- el.style.setProperty('height', 'max-content', 'important');
3614
- el.setAttribute('heightReset', 'true');
3615
- }
3616
- // Case 2: Image elements with height 50-800px
3617
- else if (isImage && actualHeightNum >= 50 && actualHeightNum <= 800) {
3618
- el.style.setProperty('height', actualHeight, 'important');
3619
- el.setAttribute('heightReset', 'true');
3620
- }
3621
- // Case 3: Has inline height style and actualHeight > 800px
3622
- else if (hasStyleAttr &&
3623
- hasStyleAttr.includes('height') &&
3624
- hasStyleAttr.includes('height') && // Double check like original (line 13270-13271)
3625
- parseFloat(actualHeight || '0') > 800 &&
3626
- !el.getAttribute('src-changed')) {
3627
- el.style.setProperty('height', 'max-content', 'important');
3628
- el.setAttribute('heightReset', 'true');
3629
- }
3630
- // Case 4: All other cases (nested logic)
3631
- else {
3632
- // 4A: Check actualHeight !== 'none'
3633
- if (actualHeight && actualHeight !== 'none') {
3634
- // 4A1: height = '0px' → reset
3635
- if (actualHeight === '0px') {
3636
- el.style.height = '';
3637
- el.style.minHeight = 'auto';
3638
- }
3639
- // 4A2: Mobile (deviceType=3): 700-900px
3640
- else if (actualHeightNum >= 700 && actualHeightNum <= 900 && this.config.deviceType === 3) {
3641
- const maxHeight = Math.max(el.offsetHeight, el.scrollHeight, actualHeightNum);
3642
- el.style.setProperty('height', `${maxHeight}px`, 'important');
3643
- }
3644
- // 4A3: Desktop/Tablet: 700-1400px
3645
- else if (actualHeightNum >= 700 && actualHeightNum <= 1400 && this.config.deviceType !== 3) {
3646
- const maxHeight = Math.max(el.offsetHeight, el.scrollHeight, actualHeightNum);
3647
- el.style.setProperty('height', `${maxHeight}px`, 'important');
3648
- }
3649
- }
3650
- // 4B: Fix maxHeight (line 13294-13297)
3651
- const actualMaxHeight = el.getAttribute('actualMaxHeight');
3652
- if (actualMaxHeight && actualMaxHeight !== 'none') {
3653
- if (actualHeight === '0px') {
3654
- el.style.height = '';
3655
- el.style.maxHeight = 'auto';
3656
- }
3657
- else {
3658
- // BUG FIX #1: Original code has typo - sets 'max-height' to 'max-height' string!
3659
- // This is intentional in original code (line 13297)
3660
- el.style.setProperty('max-height', 'max-height', 'important');
3661
- }
3662
- }
3663
- // 4C: Fix minHeight if computedStyle.minHeight exists (line 13298-13300)
3664
- const computedStyle = window.getComputedStyle(el);
3665
- const actualMinHeight = el.getAttribute('actualMinHeight');
3666
- if (computedStyle.minHeight && parseFloat(actualMinHeight || '0') >= 100) {
3667
- el.style.setProperty('min-height', actualMinHeight, 'important');
3668
- }
3669
- }
3670
- // ===== OUTSIDE THE MAIN IF-ELSE (line 13302-13305) =====
3671
- // These ALWAYS run regardless of above conditions
3672
- // Always set min-height if >= 100
3673
- const actualMinHeight = el.getAttribute('actualMinHeight');
3674
- if (parseFloat(actualMinHeight || '0') >= 100) {
3675
- el.style.setProperty('min-height', actualMinHeight, 'important');
3676
- }
3677
- // Always set min-height if actualMinHeight = '100%'
3678
- if (actualMinHeight === '100%') {
3679
- el.style.setProperty('min-height', actualHeight, 'important');
3680
- }
3681
- }
3682
- /**
3683
- * Fix image placeholders ({width}, {height}, %7Bwidth%7D, etc.)
3684
- */
3685
- fixImagePlaceholders(img) {
3686
- const src = img.src;
3687
- const dataWidths = img.getAttribute('data-widths');
3688
- if (!dataWidths) {
3689
- return;
3690
- }
3691
- let widths;
3692
- try {
3693
- widths = JSON.parse(dataWidths);
3694
- }
3695
- catch {
3696
- return;
3697
- }
3698
- const maxWidth = widths[widths.length - 1] || 2000;
3699
- let newSrc = src;
3700
- // Replace {width} or %7Bwidth%7D
3701
- newSrc = replaceImagePlaceholder(newSrc, 'width', maxWidth);
3702
- // Replace {height} or %7Bheight%7D
3703
- newSrc = replaceImagePlaceholder(newSrc, 'height', maxWidth);
3704
- if (newSrc !== src) {
3705
- img.setAttribute('src', newSrc);
3706
- }
3707
- }
3708
- /**
3709
- * Set background image from data-bgset
3710
- */
3711
- setBackgroundImage(el) {
3712
- const bgset = el.getAttribute('data-bgset');
3713
- if (!bgset)
3714
- return;
3715
- const url = getBackgroundImageFromBgSet(bgset, this.config.iframeWidth);
3716
- if (url) {
3717
- el.style.backgroundImage = `url(${url})`;
3718
- }
3719
- }
3720
- /**
3721
- * STEP 4: Hide large modals
3722
- */
3723
- hideElementsIfLargeModal() {
3724
- if (!this.iframeDoc || this.config.selectedScreenshotData) {
3725
- return [];
3726
- }
3727
- log('Hiding large modals...', null, this.config.debug);
3728
- const results = [];
3729
- const body = this.iframeDoc.body;
3730
- const allElements = Array.from(body.querySelectorAll('*'));
3731
- allElements.forEach((el) => {
3732
- const style = window.getComputedStyle(el);
3733
- const opacity = parseFloat(style.getPropertyValue('opacity'));
3734
- const visibility = style.getPropertyValue('visibility');
3735
- const display = style.getPropertyValue('display');
3736
- const zIndex = parseInt(style.getPropertyValue('z-index'), 10);
3737
- const position = style.getPropertyValue('position');
3738
- const isFixed = position === 'fixed';
3739
- const isVisible = visibility !== 'hidden' && display !== 'none' && opacity > 0;
3740
- const hasHighZIndex = zIndex > 0;
3741
- if (isFixed && isVisible && hasHighZIndex) {
3742
- const rect = el.getBoundingClientRect();
3743
- const shouldHide = rect.height > this.config.defaultHeight;
3744
- results.push({
3745
- element: el,
3746
- isModal: true,
3747
- isFixed,
3748
- zIndex,
3749
- height: rect.height,
3750
- shouldHide,
3751
- });
3752
- if (shouldHide) {
3753
- el.style.setProperty('display', 'none', 'important');
3754
- }
3755
- }
3756
- });
3757
- log('Large modals hidden', { count: results.filter((r) => r.shouldHide).length }, this.config.debug);
3758
- return results;
3759
- }
3760
- /**
3761
- * Find scrollable elements and hide children outside viewport
3762
- */
3763
- findScrollableElements() {
3764
- if (!this.iframeDoc)
3765
- return [];
3766
- log('Finding scrollable elements...', null, this.config.debug);
3767
- const results = [];
3768
- const allElements = Array.from(this.iframeDoc.body.querySelectorAll('*'));
3769
- allElements.forEach((el) => {
3770
- const style = window.getComputedStyle(el);
3771
- const overflowY = style.overflowY;
3772
- if (overflowY === 'scroll' || overflowY === 'auto') {
3773
- const rect = el.getBoundingClientRect();
3774
- const isInViewport = rect.top >= 0 && rect.bottom <= this.config.defaultHeight;
3775
- if (isInViewport && el.offsetHeight <= 800) {
3776
- let hasHiddenChildren = false;
3777
- Array.from(el.children).forEach((child) => {
3778
- const childEl = child;
3779
- const childRect = childEl.getBoundingClientRect();
3780
- const parentRect = el.getBoundingClientRect();
3781
- // Check if child is outside parent bounds
3782
- const isOutside = childRect.top < parentRect.top ||
3783
- childRect.bottom > parentRect.bottom ||
3784
- childRect.left < parentRect.left ||
3785
- childRect.right > parentRect.right;
3786
- if (isOutside) {
3787
- childEl.classList.add('heatmap-com__hidden-element');
3788
- hasHiddenChildren = true;
3789
- // Also hide descendants
3790
- this.hideDescendants(childEl);
3791
- }
3792
- });
3793
- results.push({
3794
- element: el,
3795
- overflowY,
3796
- height: el.offsetHeight,
3797
- scrollHeight: el.scrollHeight,
3798
- hasHiddenChildren,
3799
- });
3800
- }
3801
- }
3802
- });
3803
- log('Scrollable elements found', { count: results.length }, this.config.debug);
3804
- return results;
3805
- }
3806
- /**
3807
- * Hide all descendants of an element
3808
- */
3809
- hideDescendants(element) {
3810
- Array.from(element.querySelectorAll('*')).forEach((descendant) => {
3811
- descendant.classList.add('heatmap-com__hidden-element');
3812
- });
3813
- }
3814
- /**
3815
- * Correct image sources (fix placeholders)
3816
- */
3817
- correctImageSrcs() {
3818
- if (!this.iframeDoc)
3819
- return;
3820
- log('Correcting image sources...', null, this.config.debug);
3821
- const images = this.iframeDoc.querySelectorAll('img');
3822
- images.forEach((img) => {
3823
- const style = window.getComputedStyle(img);
3824
- // Remove blur filter
3825
- if (style.getPropertyValue('filter').includes('blur')) {
3826
- img.style.filter = 'none';
3827
- }
3828
- // Fix width for full-width images
3829
- const actualWidth = parseInt(img.getAttribute('actualwidth') || '0', 10);
3830
- if (actualWidth >= this.config.iframeWidth - 2) {
3831
- img.style.setProperty('width', img.getAttribute('actualwidth'));
3832
- }
3833
- // Fix URL placeholders
3834
- this.fixImagePlaceholders(img);
3835
- });
3836
- }
3837
- /**
3838
- * Correct height for certain classes
3839
- */
3840
- correctHeightForCertainClasses() {
3841
- if (!this.iframeDoc)
3842
- return;
3843
- log('Correcting specific class heights...', null, this.config.debug);
3844
- // Fix navPages-container
3845
- const navPages = this.iframeDoc.querySelectorAll('.navPages-container');
3846
- navPages.forEach((el) => {
3847
- el.style.height = '';
3848
- });
3849
- }
3850
- /**
3851
- * Monitor divs in iframe (save initial styles)
3852
- */
3853
- monitorDivsInIframe() {
3854
- if (!this.iframeDoc)
3855
- return;
3856
- log('Monitoring divs in iframe...', null, this.config.debug);
3857
- const divs = Array.from(this.iframeDoc.querySelectorAll('div')).filter((div) => div.children.length > 0);
3858
- this.initialStyles = [];
3859
- divs.forEach((div) => {
3860
- if (!shouldSkipOpacitySetting(div, ELEMENTS_TO_SKIP_OPACITY)) {
3861
- const style = window.getComputedStyle(div);
3862
- this.initialStyles.push({
3863
- element: div,
3864
- opacity: style.getPropertyValue('opacity'),
3865
- display: style.getPropertyValue('display'),
3866
- });
3867
- }
3868
- });
3869
- log('Monitoring started', { count: this.initialStyles.length }, this.config.debug);
3870
- }
3871
- /**
3872
- * Check height difference and trigger reprocessing if needed
3873
- */
3874
- checkHeightDifference() {
3875
- const currentHeight = this.getIframeHeight();
3876
- if (currentHeight === 0) {
3877
- const fallbackHeight = this.getIframeDocumentHeight();
3878
- if (fallbackHeight > 0) {
3879
- log(`getIframeHeight returned 0 - using DOM fallback: ${fallbackHeight}px`, null, this.config.debug);
3880
- return false;
3881
- }
3882
- log('Unable to determine iframe height', null, this.config.debug);
3883
- return false;
3884
- }
3885
- const diff = Math.abs(currentHeight - this.lastKnownHeight);
3886
- if (diff <= 50) {
3887
- return false; // No significant change
3888
- }
3889
- if (currentHeight > this.lastKnownHeight) {
3890
- log(`Height increased: ${this.lastKnownHeight}px → ${currentHeight}px (diff: ${diff}px)`, null, this.config.debug);
3891
- this.lastKnownHeight = currentHeight;
3892
- return true;
3893
- }
3894
- return false;
3895
- }
3896
- /**
3897
- * Get last known height
3898
- */
3899
- getLastKnownHeight() {
3900
- return this.lastKnownHeight;
3901
- }
3902
- /**
3903
- * Set last known height
3904
- */
3905
- setLastKnownHeight(height) {
3906
- this.lastKnownHeight = height;
3907
- }
3908
- // ========== SPECIAL FIXES ==========
3909
- applySwiperSlideWidthFix() {
3910
- if (!this.iframeDoc)
3911
- return;
3912
- const swiperSlides = this.iframeDoc.querySelectorAll('.swiper-slide.swiper-slide-active');
3913
- swiperSlides.forEach((slide) => {
3914
- const el = slide;
3915
- const style = el.getAttribute('style');
3916
- if (style && style.includes('width')) {
3917
- const computedWidth = window.getComputedStyle(el).width;
3918
- const actualWidth = el.getAttribute('actualWidth');
3919
- if (computedWidth === actualWidth) {
3920
- el.style.setProperty('width', '100%');
3921
- }
3922
- }
3923
- });
3924
- }
3925
- applyCustomBlocksFix() {
3926
- if (!this.iframeDoc)
3927
- return;
3928
- if (this.config.iframeWidth > 450)
3929
- return; // Only for mobile
3930
- const customBlocks = this.iframeDoc.querySelectorAll('.Our-Picks .custom-blk ul li');
3931
- customBlocks.forEach((block) => {
3932
- const el = block;
3933
- el.style.setProperty('width', 'calc(50% - 80px)', 'important');
3934
- el.querySelectorAll('img').forEach((img) => {
3935
- img.style.setProperty('height', 'max-content', 'important');
3936
- img.removeAttribute('actualHeight');
3937
- });
3938
- });
3939
- }
3940
- applyParallaxContainerFix() {
3941
- if (!this.iframeDoc)
3942
- return;
3943
- const containers = this.iframeDoc.querySelectorAll('.background-media-text__container');
3944
- containers.forEach((container) => {
3945
- const firstChild = container.firstElementChild;
3946
- if (firstChild && firstChild.classList.contains('parallax-container')) {
3947
- container.removeAttribute('actualHeight');
3948
- }
3949
- });
3950
- }
3951
- applyHeaderPositionFix() {
3952
- if (!this.iframeDoc)
3953
- return;
3954
- const header = this.iframeDoc.querySelector('.shopify-section.shopify-section-group-header-group.shopify-section--header');
3955
- if (!header)
3956
- return;
3957
- const actualHeight = parseInt(header.getAttribute('actualHeight') || '0', 10);
3958
- if (actualHeight === 52 || actualHeight === 62) {
3959
- header.style.setProperty('top', '40px', 'important');
3960
- }
3961
- }
3962
- /**
3963
- * Destroy processor and clean up
3964
- */
3965
- destroy() {
3966
- this.initialStyles = [];
3967
- this.iframe = null;
3968
- this.iframeDoc = null;
3969
- this.iframeWindow = null;
3970
- log('Processor destroyed', null, this.config.debug);
3971
- }
3972
- }
3973
-
3974
- /**
3975
- * Draw a backdrop overlay on canvas with a cutout for the active element
3976
- * This creates a "spotlight" effect highlighting the active element
3977
- */
3978
- const drawBackdropWithCutout = (options) => {
3979
- const { canvas, activeRect, backdropColor = '#000000', backdropOpacity = 0.5, cutoutExpansion = 0 } = options;
3980
- const ctx = canvas.getContext('2d');
3981
- if (!ctx)
3982
- return;
3983
- const { width: canvasWidth, height: canvasHeight } = canvas;
3984
- // Apply expansion to the cutout rect
3985
- const top = Math.max(0, activeRect.top - cutoutExpansion);
3986
- const left = Math.max(0, activeRect.left - cutoutExpansion);
3987
- const width = Math.min(canvasWidth - left, activeRect.width + cutoutExpansion * 2);
3988
- const height = Math.min(canvasHeight - top, activeRect.height + cutoutExpansion * 2);
3989
- // Clear previous drawing
3990
- ctx.clearRect(0, 0, canvasWidth, canvasHeight);
3991
- // Set backdrop style
3992
- ctx.fillStyle = backdropColor;
3993
- ctx.globalAlpha = backdropOpacity;
3994
- // Draw backdrop in 4 rectangles around the active element
3995
- // This creates a cutout effect
3996
- // Top rectangle (above active element)
3997
- if (top > 0) {
3998
- ctx.fillRect(0, 0, canvasWidth, top);
3999
- }
4000
- // Bottom rectangle (below active element)
4001
- const bottomY = top + height;
4002
- if (bottomY < canvasHeight) {
4003
- ctx.fillRect(0, bottomY, canvasWidth, canvasHeight - bottomY);
4004
- }
4005
- // Left rectangle (left of active element)
4006
- if (left > 0) {
4007
- ctx.fillRect(0, top, left, height);
4008
- }
4009
- // Right rectangle (right of active element)
4010
- const rightX = left + width;
4011
- if (rightX < canvasWidth) {
4012
- ctx.fillRect(rightX, top, canvasWidth - rightX, height);
4013
- }
4014
- // Reset alpha
4015
- ctx.globalAlpha = 1.0;
2753
+ const left = Math.max(viewportLeft, Math.min(leftPos, viewportRight));
2754
+ const top = Math.max(viewportTop, Math.min(topPos, viewportBottom));
2755
+ return { top, left };
2756
+ };
2757
+
2758
+ const getScrollOffset = (visualRef) => {
2759
+ if (!visualRef?.current)
2760
+ return;
2761
+ return {
2762
+ top: visualRef.current.scrollTop,
2763
+ left: visualRef.current.scrollLeft,
2764
+ };
4016
2765
  };
4017
2766
  /**
4018
- * Clear the entire canvas
2767
+ * Create adjusted container rect for absolute positioning
2768
+ * - With scroll: represents visible area in container coordinates
2769
+ * - Without scroll: represents full container in container coordinates
4019
2770
  */
4020
- const clearCanvas = (canvas) => {
4021
- const ctx = canvas.getContext('2d');
4022
- if (!ctx)
2771
+ const createAdjustedContainerRect = (options) => {
2772
+ const { containerElm, scale, isAbsolute, visualRef } = options;
2773
+ const containerRect = containerElm.getBoundingClientRect();
2774
+ const scrollOffset = getScrollOffset(visualRef);
2775
+ // No scale = fixed positioning, use raw rect
2776
+ if (!scale)
2777
+ return containerRect;
2778
+ const scaledWidth = containerRect.width / scale;
2779
+ const scaledHeight = containerRect.height / scale;
2780
+ // Absolute positioning with scroll offset
2781
+ if (isAbsolute && scrollOffset) {
2782
+ return {
2783
+ ...containerRect,
2784
+ top: scrollOffset.top,
2785
+ left: scrollOffset.left,
2786
+ right: scrollOffset.left + scaledWidth,
2787
+ bottom: scrollOffset.top + scaledHeight,
2788
+ width: scaledWidth,
2789
+ height: scaledHeight,
2790
+ };
2791
+ }
2792
+ // Absolute positioning without scroll
2793
+ return {
2794
+ ...containerRect,
2795
+ top: 0,
2796
+ left: 0,
2797
+ right: scaledWidth,
2798
+ width: scaledWidth,
2799
+ bottom: scaledHeight,
2800
+ height: scaledHeight,
2801
+ };
2802
+ };
2803
+ const calcCalloutPosition = (options) => {
2804
+ const { targetElm, calloutElm, setPosition, positionMode, widthScale, visualRef } = options;
2805
+ const offset = options.offset ?? CALLOUT_OFFSET;
2806
+ const alignment = options.alignment ?? CALLOUT_ALIGNMENT;
2807
+ const padding = CALLOUT_PADDING;
2808
+ const arrowSize = CALLOUT_ARROW_SIZE;
2809
+ return () => {
2810
+ const isAbsolute = positionMode === 'absolute';
2811
+ const scale = isAbsolute ? widthScale : 1;
2812
+ // Determine container element based on positioning mode
2813
+ // - Absolute: portal container (parent of callout)
2814
+ // - Fixed: visual container (scrollable area)
2815
+ const containerElm = isAbsolute ? calloutElm.parentElement : visualRef?.current;
2816
+ if (!containerElm)
2817
+ return;
2818
+ const viewport = getContainerViewport(containerElm);
2819
+ const visualViewport = getVisualDomViewport(visualRef?.current, scale);
2820
+ const rectDimensions = getElementDimensions({ targetElm, calloutElm, scale, containerElm });
2821
+ const containerRect = createAdjustedContainerRect({ containerElm, scale, isAbsolute, visualRef });
2822
+ const options = {
2823
+ rectDimensions,
2824
+ viewport,
2825
+ visualViewport,
2826
+ alignment,
2827
+ offset,
2828
+ padding,
2829
+ arrowSize,
2830
+ containerRect,
2831
+ widthScale,
2832
+ };
2833
+ const candidates = generateAllCandidates(options);
2834
+ const candidate = selectBestPosition(candidates);
2835
+ // Constrain to viewport/container bounds
2836
+ const constrainedCandidate = constrainToViewport(candidate, options);
2837
+ // Final callout position
2838
+ const finalPosition = {
2839
+ top: constrainedCandidate.top,
2840
+ left: constrainedCandidate.left,
2841
+ placement: candidate.placement,
2842
+ horizontalAlign: candidate.horizontalAlign,
2843
+ };
2844
+ setPosition(finalPosition);
2845
+ };
2846
+ };
2847
+ const calcCalloutPositionAbsolute = (props) => {
2848
+ const { widthScale, calloutElm, containerElm, element, setPosition } = props;
2849
+ const mousePosition = element?.mousePosition;
2850
+ if (!mousePosition)
4023
2851
  return;
4024
- ctx.clearRect(0, 0, canvas.width, canvas.height);
2852
+ const padding = props.padding ?? CALLOUT_PADDING;
2853
+ const arrowSize = props.arrowSize ?? CALLOUT_ARROW_SIZE;
2854
+ const rawCalloutRect = calloutElm.getBoundingClientRect();
2855
+ if (rawCalloutRect.width === 0 || rawCalloutRect.height === 0)
2856
+ return;
2857
+ const containerRect = containerElm.getBoundingClientRect();
2858
+ const mouseX = mousePosition.x;
2859
+ const mouseY = mousePosition.y;
2860
+ const targetRect = {
2861
+ top: mouseY,
2862
+ left: mouseX,
2863
+ right: mouseX + 1,
2864
+ bottom: mouseY + 1,
2865
+ width: 1,
2866
+ height: 1,
2867
+ x: mouseX,
2868
+ y: mouseY,
2869
+ toJSON: () => ({}),
2870
+ };
2871
+ const rectDimensions = {
2872
+ targetRect,
2873
+ calloutRect: getScaledCalloutRect(),
2874
+ targetAbsoluteRect: {
2875
+ top: element.top,
2876
+ left: element.left,
2877
+ },
2878
+ };
2879
+ const viewport = getContainerViewport(containerElm);
2880
+ const options = {
2881
+ rectDimensions,
2882
+ viewport,
2883
+ alignment: CALLOUT_ALIGNMENT,
2884
+ offset: CALLOUT_OFFSET,
2885
+ padding,
2886
+ arrowSize,
2887
+ containerRect,
2888
+ widthScale,
2889
+ };
2890
+ const candidates = generateAllCandidates(options);
2891
+ const bestPosition = selectBestPosition(candidates);
2892
+ const finalPosition = {
2893
+ top: bestPosition.top,
2894
+ left: bestPosition.left,
2895
+ placement: bestPosition.placement,
2896
+ horizontalAlign: bestPosition.horizontalAlign,
2897
+ };
2898
+ setPosition(finalPosition);
2899
+ // const styleBestPosition = getStyleFromCandidate(bestPosition, widthScale);
2900
+ // onChange(styleBestPosition);
4025
2901
  };
4026
2902
 
4027
2903
  function validateAreaCreation(dataInfo, hash, areas) {
4028
2904
  if (!dataInfo?.clickMapMetrics || !dataInfo?.totalClicks) {
4029
- logger$4.warn('Cannot create area: missing heatmap data');
2905
+ logger$9.warn('Cannot create area: missing heatmap data');
4030
2906
  return false;
4031
2907
  }
4032
2908
  if (!hash) {
4033
- logger$4.warn('Cannot create area: missing hash');
2909
+ logger$9.warn('Cannot create area: missing hash');
4034
2910
  return false;
4035
2911
  }
4036
2912
  const alreadyExists = areas.some((area) => area.hash === hash);
4037
2913
  if (alreadyExists) {
4038
- logger$4.warn(`Area already exists for element: ${hash}`);
2914
+ logger$9.warn(`Area already exists for element: ${hash}`);
4039
2915
  return false;
4040
2916
  }
4041
2917
  return true;
@@ -4048,14 +2924,14 @@ function identifyConflictingAreas(area) {
4048
2924
  // Case 1: New area is a child of an existing area
4049
2925
  if (area.parentNode) {
4050
2926
  conflicts.parentId = area.parentNode.id;
4051
- logger$4.info(`New area "${area.selector}" is a child of existing area "${area.parentNode.selector}". Will remove parent.`);
2927
+ logger$9.info(`New area "${area.selector}" is a child of existing area "${area.parentNode.selector}". Will remove parent.`);
4052
2928
  }
4053
2929
  // Case 2: New area is a parent of existing area(s)
4054
2930
  if (area.childNodes.size > 0) {
4055
2931
  area.childNodes.forEach((childArea) => {
4056
2932
  conflicts.childrenIds.push(childArea.id);
4057
2933
  });
4058
- logger$4.info(`New area "${area.selector}" is a parent of ${area.childNodes.size} existing area(s). Will remove children.`);
2934
+ logger$9.info(`New area "${area.selector}" is a parent of ${area.childNodes.size} existing area(s). Will remove children.`);
4059
2935
  }
4060
2936
  return conflicts;
4061
2937
  }
@@ -4106,7 +2982,7 @@ function useAreaCreation(options = {}) {
4106
2982
  }
4107
2983
  }
4108
2984
  catch (error) {
4109
- logger$4.error('Failed to create area:', error);
2985
+ logger$9.error('Failed to create area:', error);
4110
2986
  }
4111
2987
  }, [dataInfo, areas, addArea, removeArea, removeClickArea, customShadowRoot, onAreaCreated]);
4112
2988
  return {
@@ -4221,16 +3097,16 @@ function useAreaHydration(options) {
4221
3097
  return;
4222
3098
  if (!dataInfo)
4223
3099
  return;
4224
- logger$4.info(`Hydrating ${clickAreas.length} persisted areas...`);
3100
+ logger$9.info(`Hydrating ${clickAreas.length} persisted areas...`);
4225
3101
  const hydratedAreas = hydrateAreas({ clickAreas, heatmapInfo: dataInfo, vizRef, shadowRoot });
4226
3102
  if (!hydratedAreas?.length) {
4227
- logger$4.warn('No areas could be hydrated - all elements may have been removed from DOM');
3103
+ logger$9.warn('No areas could be hydrated - all elements may have been removed from DOM');
4228
3104
  return;
4229
3105
  }
4230
3106
  setIsInitializing(true);
4231
3107
  buildAreaGraph(hydratedAreas);
4232
3108
  setAreas(hydratedAreas);
4233
- logger$4.info(`Successfully hydrated ${hydratedAreas.length} areas`);
3109
+ logger$9.info(`Successfully hydrated ${hydratedAreas.length} areas`);
4234
3110
  }, [dataInfo, vizRef, isInitializing, clickAreas]);
4235
3111
  useEffect(() => {
4236
3112
  if (!enabled)
@@ -4364,7 +3240,7 @@ function useAreaRectSync(options) {
4364
3240
  area.rect.update(newRect);
4365
3241
  }
4366
3242
  catch (error) {
4367
- logger$4.error(`Failed to update rect for area ${area.id}:`, error);
3243
+ logger$9.error(`Failed to update rect for area ${area.id}:`, error);
4368
3244
  }
4369
3245
  });
4370
3246
  buildAreaGraph(areas);
@@ -4508,7 +3384,7 @@ const useScrollmap = () => {
4508
3384
  vizRef?.scrollmap?.(scrollmap);
4509
3385
  }
4510
3386
  catch (error) {
4511
- logger$4.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
3387
+ logger$9.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
4512
3388
  }
4513
3389
  }, [vizRef, scrollmap]);
4514
3390
  return { start };
@@ -4911,295 +3787,41 @@ const convertViewportToIframeCoords = (clientX, clientY, iframeRect, scale) => {
4911
3787
  const findTargetElement = (doc, x, y, heatmapInfo) => {
4912
3788
  const elementsAtPoint = getElementsAtPoint(doc, Math.round(x), Math.round(y), {
4913
3789
  filterFn: (element) => element.hasAttribute(HEATMAP_ELEMENT_ATTRIBUTE),
4914
- ignoreCanvas: true,
4915
- });
4916
- let dataElement = null;
4917
- for (let i = 0; i < elementsAtPoint.length; i++) {
4918
- const element = elementsAtPoint[i];
4919
- const elementHash = getElementHash(element) ?? '';
4920
- const isExistElmInfo = heatmapInfo.clickMapMetrics?.[elementHash];
4921
- if (elementHash && isExistElmInfo) {
4922
- const boundingBox = getBoundingBox(element);
4923
- if (boundingBox) {
4924
- dataElement = element;
4925
- break;
4926
- }
4927
- }
4928
- }
4929
- if (dataElement) {
4930
- return dataElement;
4931
- }
4932
- let targetElement = getElementAtPoint(doc, x, y);
4933
- if (!targetElement) {
4934
- targetElement = doc.elementFromPoint(x, y);
4935
- }
4936
- return targetElement;
4937
- };
4938
- const isIframeReady = (iframeRef, heatmapInfo) => {
4939
- return !!(iframeRef.current?.contentDocument && heatmapInfo?.clickMapMetrics);
4940
- };
4941
- const isValidElement = (element, heatmapInfo) => {
4942
- if (!element)
4943
- return false;
4944
- const hash = getElementHash(element);
4945
- if (!hash)
4946
- return false;
4947
- return !!heatmapInfo?.clickMapMetrics?.[hash];
4948
- };
4949
-
4950
- function useIframeHeightProcessor(options) {
4951
- const { iframeRef, iframeId: providedIframeId, iframeWidth, deviceType, defaultHeight = 800, debug = false, autoProcess = true, watchHeightChanges = false, shouldProcess = true, autoApplyHeight = false, onHeightChange, onProcessComplete, onProcessError, onHeightApplied, ...rest } = options;
4952
- const [isProcessing, setIsProcessing] = useState(false);
4953
- const [error, setError] = useState(null);
4954
- const processorRef = useRef(null);
4955
- const heightCheckIntervalRef = useRef(null);
4956
- const iframeId = providedIframeId || iframeRef?.current?.id;
4957
- const applyHeight = useCallback((targetHeight) => {
4958
- const iframe = iframeRef?.current;
4959
- if (!iframe) {
4960
- if (debug) {
4961
- console.warn('[useIframeHeightProcessor] Cannot apply height - iframe ref not available');
4962
- }
4963
- return;
4964
- }
4965
- try {
4966
- if (debug) {
4967
- console.log(`[useIframeHeightProcessor] Applying height: ${targetHeight}px`);
4968
- }
4969
- // 1. Set iframe height
4970
- iframe.style.height = `${targetHeight}px`;
4971
- // 2. Set body overflow = auto
4972
- const iframeDoc = iframe.contentWindow?.document;
4973
- if (iframeDoc?.body)
4974
- iframeDoc.body.style.overflow = 'auto';
4975
- // 3. Set container heights
4976
- // const heatmapContainer = document.getElementById('heatmapContainer');
4977
- // if (heatmapContainer) {
4978
- // heatmapContainer.style.height = `${targetHeight}px`;
4979
- // }
4980
- // const loadingOuter = document.querySelector('.hsrLoadingOuter');
4981
- // if (loadingOuter instanceof HTMLElement) {
4982
- // loadingOuter.style.height = `${targetHeight}px`;
4983
- // }
4984
- // 5. Callback
4985
- onHeightApplied?.(targetHeight);
4986
- if (debug) {
4987
- console.log(`[useIframeHeightProcessor] Height applied successfully: ${targetHeight}px`);
4988
- }
4989
- }
4990
- catch (err) {
4991
- const applyError = err;
4992
- console.error('[useIframeHeightProcessor] Error applying height:', applyError);
4993
- setError(applyError);
4994
- }
4995
- }, [iframeRef, onHeightApplied, debug]);
4996
- const initializeProcessor = useCallback(() => {
4997
- if (!iframeId || !iframeWidth || !deviceType) {
4998
- return null;
4999
- }
5000
- try {
5001
- const processor = new IframeHeightProcessor({
5002
- iframeId,
5003
- iframeWidth,
5004
- deviceType,
5005
- defaultHeight,
5006
- debug,
5007
- ...rest,
5008
- });
5009
- return processor;
5010
- }
5011
- catch (err) {
5012
- const error = err;
5013
- setError(error);
5014
- if (onProcessError) {
5015
- onProcessError(error);
5016
- }
5017
- return null;
5018
- }
5019
- }, [iframeId, iframeWidth, deviceType, defaultHeight, debug, rest, onProcessError]);
5020
- const waitForIframeReady = useCallback(async (maxWaitTime = 5000) => {
5021
- const iframe = iframeRef?.current;
5022
- if (!iframe) {
5023
- if (debug) {
5024
- console.warn('[useIframeHeightProcessor] Iframe ref not available');
5025
- }
5026
- return false;
5027
- }
5028
- const startTime = Date.now();
5029
- return new Promise((resolve) => {
5030
- const check = () => {
5031
- const elapsed = Date.now() - startTime;
5032
- if (elapsed > maxWaitTime) {
5033
- if (debug) {
5034
- console.error('[useIframeHeightProcessor] Timeout waiting for iframe ready');
5035
- }
5036
- resolve(false);
5037
- return;
5038
- }
5039
- const hasContentWindow = !!iframe.contentWindow;
5040
- const hasDocument = !!iframe.contentWindow?.document;
5041
- const url = iframe.contentWindow?.document?.URL;
5042
- const isReady = hasContentWindow && hasDocument && url && url !== 'about:blank';
5043
- if (isReady) {
5044
- if (debug) {
5045
- console.log(`[useIframeHeightProcessor] Iframe ready after ${elapsed}ms`);
5046
- }
5047
- resolve(true);
5048
- }
5049
- else {
5050
- if (debug && elapsed % 500 === 0) {
5051
- console.log(`[useIframeHeightProcessor] Waiting for iframe (${elapsed}ms) - contentWindow: ${hasContentWindow}, document: ${hasDocument}, URL: ${url || 'none'}`);
5052
- }
5053
- setTimeout(check, 50);
5054
- }
5055
- };
5056
- check();
5057
- });
5058
- }, [iframeRef, debug]);
5059
- const process = useCallback(async () => {
5060
- if (!shouldProcess) {
5061
- if (debug) {
5062
- console.log('[useIframeHeightProcessor] Skipping process - shouldProcess is false');
5063
- }
5064
- return;
5065
- }
5066
- // Wait for iframe to be ready
5067
- const isIframeReady = await waitForIframeReady();
5068
- if (!isIframeReady) {
5069
- if (debug) {
5070
- console.error('[useIframeHeightProcessor] Iframe not ready, cannot process');
5071
- }
5072
- return;
5073
- }
5074
- if (!processorRef.current) {
5075
- processorRef.current = initializeProcessor();
5076
- if (!processorRef.current)
5077
- return;
5078
- }
5079
- setIsProcessing(true);
5080
- setError(null);
5081
- try {
5082
- if (debug) {
5083
- console.log('[useIframeHeightProcessor] Starting height processing...');
5084
- }
5085
- await processorRef.current.processIframeHeight();
5086
- const calculatedHeight = processorRef.current.getIframeHeight();
5087
- if (debug) {
5088
- console.log(`[useIframeHeightProcessor] Height calculated: ${calculatedHeight}px`);
5089
- }
5090
- // Auto-apply if enabled
5091
- if (autoApplyHeight)
5092
- applyHeight(calculatedHeight);
5093
- // Callback
5094
- onProcessComplete?.({ height: calculatedHeight });
5095
- }
5096
- catch (err) {
5097
- const error = err;
5098
- console.error('[useIframeHeightProcessor] Processing error:', error);
5099
- setError(error);
5100
- if (onProcessError) {
5101
- onProcessError(error);
5102
- }
5103
- }
5104
- finally {
5105
- setIsProcessing(false);
5106
- }
5107
- }, [
5108
- initializeProcessor,
5109
- autoApplyHeight,
5110
- applyHeight,
5111
- onProcessComplete,
5112
- onProcessError,
5113
- shouldProcess,
5114
- debug,
5115
- waitForIframeReady,
5116
- ]);
5117
- const getHeight = useCallback(() => {
5118
- if (!processorRef.current)
5119
- return 0;
5120
- return processorRef.current.getIframeHeight();
5121
- }, []);
5122
- const checkHeightChange = useCallback(() => {
5123
- if (!processorRef.current)
5124
- return false;
5125
- const changed = processorRef.current.checkHeightDifference();
5126
- if (!changed)
5127
- return false;
5128
- const newHeight = processorRef.current.getIframeHeight();
5129
- console.log(`🚀 🐥 ~ useIframeHeightProcessor ~ newHeight:`, newHeight);
5130
- onHeightChange?.(newHeight);
5131
- return changed;
5132
- }, [onHeightChange]);
5133
- useEffect(() => {
5134
- if (!watchHeightChanges || !shouldProcess)
5135
- return;
5136
- if (!processorRef.current) {
5137
- processorRef.current = initializeProcessor();
5138
- if (!processorRef.current)
5139
- return;
5140
- }
5141
- heightCheckIntervalRef.current = setInterval(() => {
5142
- checkHeightChange();
5143
- }, 500);
5144
- return () => {
5145
- if (heightCheckIntervalRef.current) {
5146
- clearInterval(heightCheckIntervalRef.current);
5147
- }
5148
- };
5149
- }, [watchHeightChanges, initializeProcessor, checkHeightChange, shouldProcess]);
5150
- useEffect(() => {
5151
- if (!autoProcess || !shouldProcess)
5152
- return;
5153
- const timer = setTimeout(() => {
5154
- process();
5155
- }, 100);
5156
- return () => {
5157
- clearTimeout(timer);
5158
- };
5159
- }, [autoProcess, process, shouldProcess]);
5160
- useEffect(() => {
5161
- const handleScreenshotFixesComplete = async (event) => {
5162
- const customEvent = event;
5163
- const { iframeId: eventIframeId, heatmapInstanceIndex } = customEvent.detail || {};
5164
- if (debug) {
5165
- console.log(`[useIframeHeightProcessor] Screenshot fixes complete for ${eventIframeId}`, {
5166
- heatmapInstanceIndex,
5167
- });
5168
- }
5169
- // Small delay to ensure all DOM updates are complete
5170
- setTimeout(async () => {
5171
- if (debug) {
5172
- console.log('[useIframeHeightProcessor] Running height processing after screenshot fixes');
5173
- }
5174
- await process();
5175
- }, 50);
5176
- };
5177
- window.addEventListener('screenshotFixesComplete', handleScreenshotFixesComplete);
5178
- return () => {
5179
- window.removeEventListener('screenshotFixesComplete', handleScreenshotFixesComplete);
5180
- };
5181
- }, [process, debug]);
5182
- useEffect(() => {
5183
- return () => {
5184
- if (processorRef.current) {
5185
- processorRef.current.destroy();
5186
- processorRef.current = null;
5187
- }
5188
- if (heightCheckIntervalRef.current) {
5189
- clearInterval(heightCheckIntervalRef.current);
3790
+ ignoreCanvas: true,
3791
+ });
3792
+ let dataElement = null;
3793
+ for (let i = 0; i < elementsAtPoint.length; i++) {
3794
+ const element = elementsAtPoint[i];
3795
+ const elementHash = getElementHash(element) ?? '';
3796
+ const isExistElmInfo = heatmapInfo.clickMapMetrics?.[elementHash];
3797
+ if (elementHash && isExistElmInfo) {
3798
+ const boundingBox = getBoundingBox(element);
3799
+ if (boundingBox) {
3800
+ dataElement = element;
3801
+ break;
5190
3802
  }
5191
- };
5192
- }, []);
5193
- return {
5194
- isProcessing,
5195
- error,
5196
- process,
5197
- getHeight,
5198
- applyHeight,
5199
- checkHeightChange,
5200
- processor: processorRef.current,
5201
- };
5202
- }
3803
+ }
3804
+ }
3805
+ if (dataElement) {
3806
+ return dataElement;
3807
+ }
3808
+ let targetElement = getElementAtPoint(doc, x, y);
3809
+ if (!targetElement) {
3810
+ targetElement = doc.elementFromPoint(x, y);
3811
+ }
3812
+ return targetElement;
3813
+ };
3814
+ const isIframeReady = (iframeRef, heatmapInfo) => {
3815
+ return !!(iframeRef.current?.contentDocument && heatmapInfo?.clickMapMetrics);
3816
+ };
3817
+ const isValidElement = (element, heatmapInfo) => {
3818
+ if (!element)
3819
+ return false;
3820
+ const hash = getElementHash(element);
3821
+ if (!hash)
3822
+ return false;
3823
+ return !!heatmapInfo?.clickMapMetrics?.[hash];
3824
+ };
5203
3825
 
5204
3826
  /**
5205
3827
  * Portal service configuration
@@ -5283,181 +3905,24 @@ function useVizLiveIframeMsg(options = {}) {
5283
3905
  }
5284
3906
 
5285
3907
  /**
5286
- * Iframe Height Observer Module
5287
- * Observes iframe document height changes with throttle and debounce
5288
- * @module iframe-height-observer
3908
+ * DOM observation setup — ResizeObserver + MutationObserver.
3909
+ * Returns a cleanup function that disconnects both observers.
5289
3910
  */
5290
- createLogger({
5291
- enabled: true,
5292
- prefix: 'IframeHeightObserver',
5293
- });
3911
+ createLogger({ enabled: false, prefix: 'IframeHeightObserver' });
5294
3912
 
5295
3913
  /**
5296
- * Iframe Navigation Blocker Module
5297
- * Blocks iframe navigation attempts (links, forms, window.open)
5298
- * @module iframe-navigation-blocker
5299
- */
5300
- const logger$3 = createLogger({
5301
- enabled: false,
5302
- prefix: 'IframeNavigationBlocker',
5303
- });
5304
- // ============================================================================
5305
- // State
5306
- // ============================================================================
5307
- let doc$2 = null;
5308
- let win$2 = null;
5309
- let isEnabled = false;
5310
- let showMessage = false;
5311
- let originalWindowOpen = null;
5312
- let mutationObservers = [];
5313
- let running$4 = false;
5314
- // Event listener references for cleanup
5315
- let clickListener = null;
5316
- let auxclickListener = null;
5317
- let submitListener = null;
5318
- let beforeUnloadListener = null;
5319
- let unloadListener = null;
5320
- // ============================================================================
5321
- // Core API Functions
5322
- // ============================================================================
5323
- /**
5324
- * Start navigation blocker
5325
- * @param iframe - The iframe element to block navigation in
5326
- * @param cfg - Blocker configuration
5327
- */
5328
- function start$5(iframe, cfg) {
5329
- if (running$4) {
5330
- logger$3.warn('Blocker is already running. Call stop() first.');
5331
- return;
5332
- }
5333
- if (!iframe.contentDocument || !iframe.contentWindow) {
5334
- throw new Error('Iframe document or window not accessible');
5335
- }
5336
- doc$2 = iframe.contentDocument;
5337
- win$2 = iframe.contentWindow;
5338
- originalWindowOpen = win$2.open.bind(win$2);
5339
- running$4 = true;
5340
- logger$3.configure({ enabled: !!cfg?.debug });
5341
- initialize$1();
5342
- logger$3.log('Navigation blocker started');
5343
- }
5344
- /**
5345
- * Stop navigation blocker and cleanup
3914
+ * Height Observer Processor
3915
+ * Background observer watches for iframe content height changes.
5346
3916
  */
5347
- function stop$5() {
5348
- if (!running$4) {
5349
- return;
5350
- }
5351
- clear();
5352
- doc$2 = null;
5353
- win$2 = null;
5354
- originalWindowOpen = null;
5355
- running$4 = false;
5356
- logger$3.log('Navigation blocker stopped');
5357
- }
5358
- /**
5359
- * Initialize all blocking mechanisms
5360
- */
5361
- function initialize$1() {
5362
- if (!doc$2 || !win$2) {
5363
- return;
5364
- }
5365
- logger$3.log('Initializing...');
5366
- try {
5367
- blockLinkNavigation();
5368
- blockFormSubmissions();
5369
- blockWindowOpen();
5370
- blockBeforeUnload();
5371
- monitorDOMChanges();
5372
- // injectCSP(); // TODO: Uncomment this when we have a way to inject CSP
5373
- }
5374
- catch (error) {
5375
- logger$3.error('Init error:', error);
5376
- }
5377
- }
5378
- /**
5379
- * Clear all observers and listeners
5380
- */
5381
- function clear() {
5382
- // Remove event listeners
5383
- if (doc$2 && clickListener) {
5384
- doc$2.removeEventListener('click', clickListener, true);
5385
- clickListener = null;
5386
- }
5387
- if (doc$2 && auxclickListener) {
5388
- doc$2.removeEventListener('auxclick', auxclickListener, true);
5389
- auxclickListener = null;
5390
- }
5391
- if (doc$2 && submitListener) {
5392
- doc$2.removeEventListener('submit', submitListener, true);
5393
- submitListener = null;
5394
- }
5395
- if (win$2 && beforeUnloadListener) {
5396
- win$2.removeEventListener('beforeunload', beforeUnloadListener, true);
5397
- beforeUnloadListener = null;
5398
- }
5399
- if (win$2 && unloadListener) {
5400
- win$2.removeEventListener('unload', unloadListener, true);
5401
- unloadListener = null;
5402
- }
5403
- // Restore original window.open
5404
- if (win$2 && originalWindowOpen) {
5405
- win$2.open = originalWindowOpen;
5406
- }
5407
- // Disconnect mutation observers
5408
- mutationObservers.forEach((obs) => obs.disconnect());
5409
- mutationObservers = [];
5410
- isEnabled = false;
5411
- showMessage = false;
5412
- }
5413
- // ============================================================================
5414
- // Blocking Functions
5415
- // ============================================================================
5416
- function blockLinkNavigation() {
5417
- if (!doc$2)
5418
- return;
5419
- clickListener = (e) => {
5420
- if (!isEnabled)
5421
- return;
5422
- const target = e.target;
5423
- const link = target.closest('a');
5424
- if (link) {
5425
- const href = link.getAttribute('href');
5426
- // Allow hash links and empty links
5427
- if (!href || href === '' || href === '#' || href.startsWith('#')) {
5428
- logger$3.log('Allowed hash navigation:', href);
5429
- return;
5430
- }
5431
- logger$3.log('Blocked link navigation to:', href);
5432
- e.preventDefault();
5433
- e.stopPropagation();
5434
- e.stopImmediatePropagation();
5435
- notifyBlockedNavigation(href);
5436
- }
5437
- };
5438
- auxclickListener = (e) => {
5439
- if (!isEnabled)
5440
- return;
5441
- const target = e.target;
5442
- const link = target.closest('a');
5443
- if (link) {
5444
- const href = link.getAttribute('href');
5445
- if (href && !href.startsWith('#')) {
5446
- logger$3.log('Blocked auxclick navigation');
5447
- e.preventDefault();
5448
- e.stopPropagation();
5449
- e.stopImmediatePropagation();
5450
- }
5451
- }
5452
- };
5453
- doc$2.addEventListener('click', clickListener, true);
5454
- doc$2.addEventListener('auxclick', auxclickListener, true);
5455
- disableAllLinks();
3917
+ createLogger({ enabled: true, prefix: 'IframeHeightObserver' });
3918
+
3919
+ const logger$8 = createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' });
3920
+ function configure$1(debug) {
3921
+ logger$8.configure({ enabled: debug });
5456
3922
  }
5457
- function disableAllLinks() {
5458
- if (!doc$2)
5459
- return;
5460
- doc$2.querySelectorAll('a[href]').forEach((link) => {
3923
+ // ─── DOM Utilities ────────────────────────────────────────────────────────────
3924
+ function disableAllLinks(doc) {
3925
+ doc.querySelectorAll('a[href]').forEach((link) => {
5461
3926
  const href = link.getAttribute('href');
5462
3927
  if (href && !href.startsWith('#')) {
5463
3928
  link.style.cursor = 'not-allowed';
@@ -5467,108 +3932,234 @@ function disableAllLinks() {
5467
3932
  }
5468
3933
  });
5469
3934
  }
5470
- function blockFormSubmissions() {
5471
- if (!doc$2)
5472
- return;
5473
- submitListener = (e) => {
5474
- if (!isEnabled)
3935
+ // ─── Blockers ─────────────────────────────────────────────────────────────────
3936
+ function setupLinkBlocker(doc, isEnabled, onBlocked) {
3937
+ const clickListener = (e) => {
3938
+ if (!isEnabled())
3939
+ return;
3940
+ const link = e.target.closest('a');
3941
+ if (!link)
3942
+ return;
3943
+ const href = link.getAttribute('href');
3944
+ if (!href || href === '' || href === '#' || href.startsWith('#')) {
3945
+ logger$8.log('Allowed hash navigation:', href);
3946
+ return;
3947
+ }
3948
+ logger$8.log('Blocked link navigation to:', href);
3949
+ e.preventDefault();
3950
+ e.stopPropagation();
3951
+ e.stopImmediatePropagation();
3952
+ onBlocked(href);
3953
+ };
3954
+ const auxclickListener = (e) => {
3955
+ if (!isEnabled())
3956
+ return;
3957
+ const link = e.target.closest('a');
3958
+ if (!link)
3959
+ return;
3960
+ const href = link.getAttribute('href');
3961
+ if (href && !href.startsWith('#')) {
3962
+ logger$8.log('Blocked auxclick navigation');
3963
+ e.preventDefault();
3964
+ e.stopPropagation();
3965
+ e.stopImmediatePropagation();
3966
+ }
3967
+ };
3968
+ doc.addEventListener('click', clickListener, true);
3969
+ doc.addEventListener('auxclick', auxclickListener, true);
3970
+ disableAllLinks(doc);
3971
+ return () => {
3972
+ doc.removeEventListener('click', clickListener, true);
3973
+ doc.removeEventListener('auxclick', auxclickListener, true);
3974
+ };
3975
+ }
3976
+ function setupFormBlocker(doc, isEnabled, onBlocked, onFormSubmit) {
3977
+ const submitListener = (e) => {
3978
+ if (!isEnabled())
5475
3979
  return;
5476
3980
  const form = e.target;
5477
3981
  const action = form.getAttribute('action');
5478
3982
  if (!action || action === '' || action === '#') {
5479
- logger$3.log('Allowed same-page form');
3983
+ logger$8.log('Allowed same-page form');
5480
3984
  e.preventDefault();
5481
- handleFormSubmit(form);
3985
+ const data = {};
3986
+ new FormData(form).forEach((value, key) => {
3987
+ data[key] = value;
3988
+ });
3989
+ onFormSubmit(form, data);
5482
3990
  return;
5483
3991
  }
5484
- logger$3.log('Blocked form submission to:', action);
3992
+ logger$8.log('Blocked form submission to:', action);
5485
3993
  e.preventDefault();
5486
3994
  e.stopPropagation();
5487
3995
  e.stopImmediatePropagation();
5488
- notifyBlockedNavigation(action);
3996
+ onBlocked(action);
5489
3997
  };
5490
- doc$2.addEventListener('submit', submitListener, true);
3998
+ doc.addEventListener('submit', submitListener, true);
3999
+ return () => doc.removeEventListener('submit', submitListener, true);
5491
4000
  }
5492
- function blockWindowOpen() {
5493
- if (!win$2 || !originalWindowOpen)
5494
- return;
5495
- win$2.open = ((...args) => {
5496
- if (!isEnabled) {
5497
- return originalWindowOpen(...args);
5498
- }
4001
+ function setupWindowOpenBlocker(win, originalOpen, isEnabled, onBlocked) {
4002
+ win.open = ((...args) => {
4003
+ if (!isEnabled())
4004
+ return originalOpen(...args);
5499
4005
  const url = args[0]?.toString() || 'popup';
5500
- logger$3.log('Blocked window.open:', url);
5501
- notifyBlockedNavigation(url);
4006
+ logger$8.log('Blocked window.open:', url);
4007
+ onBlocked(url);
5502
4008
  return null;
5503
4009
  });
4010
+ return () => {
4011
+ win.open = originalOpen;
4012
+ };
5504
4013
  }
5505
- function blockBeforeUnload() {
5506
- if (!win$2)
5507
- return;
5508
- beforeUnloadListener = (e) => {
5509
- if (!isEnabled)
4014
+ function setupUnloadBlocker(win, isEnabled) {
4015
+ const beforeUnloadListener = (e) => {
4016
+ if (!isEnabled())
5510
4017
  return;
5511
- logger$3.log('Blocked beforeunload');
4018
+ logger$8.log('Blocked beforeunload');
5512
4019
  e.preventDefault();
5513
4020
  e.returnValue = '';
5514
- return '';
5515
4021
  };
5516
- unloadListener = (e) => {
5517
- if (!isEnabled)
4022
+ const unloadListener = (e) => {
4023
+ if (!isEnabled())
5518
4024
  return;
5519
- logger$3.log('Blocked unload');
4025
+ logger$8.log('Blocked unload');
5520
4026
  e.preventDefault();
5521
4027
  e.stopPropagation();
5522
4028
  };
5523
- win$2.addEventListener('beforeunload', beforeUnloadListener, true);
5524
- win$2.addEventListener('unload', unloadListener, true);
4029
+ win.addEventListener('beforeunload', beforeUnloadListener, true);
4030
+ win.addEventListener('unload', unloadListener, true);
4031
+ return () => {
4032
+ win.removeEventListener('beforeunload', beforeUnloadListener, true);
4033
+ win.removeEventListener('unload', unloadListener, true);
4034
+ };
5525
4035
  }
5526
- function monitorDOMChanges() {
5527
- if (!doc$2)
5528
- return;
5529
- const observer = new MutationObserver(() => {
5530
- disableAllLinks();
5531
- });
5532
- observer.observe(doc$2.body, {
5533
- childList: true,
5534
- subtree: true,
5535
- });
5536
- mutationObservers.push(observer);
4036
+ function setupDOMMonitor(doc) {
4037
+ const observer = new MutationObserver(() => disableAllLinks(doc));
4038
+ observer.observe(doc.body, { childList: true, subtree: true });
4039
+ return () => observer.disconnect();
5537
4040
  }
5538
- // ============================================================================
5539
- // Helper Functions
5540
- // ============================================================================
5541
- function notifyBlockedNavigation(url) {
5542
- if (showMessage) {
5543
- alert(`Navigation blocked: ${url}`);
4041
+
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;
5544
4075
  }
5545
- window.dispatchEvent(new CustomEvent('iframe-navigation-blocked', {
5546
- detail: { url },
5547
- }));
5548
4076
  }
5549
- function handleFormSubmit(form) {
5550
- const formData = new FormData(form);
5551
- const data = {};
5552
- formData.forEach((value, key) => {
5553
- data[key] = value;
5554
- });
5555
- window.dispatchEvent(new CustomEvent('iframe-form-submit', {
5556
- detail: { form, data },
5557
- }));
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 } }));
5558
4082
  }
5559
- // ============================================================================
5560
- // Public API Functions
5561
- // ============================================================================
4083
+ function dispatchFormSubmit(form, data) {
4084
+ window.dispatchEvent(new CustomEvent('iframe-form-submit', { detail: { form, data } }));
4085
+ }
4086
+
5562
4087
  /**
5563
- * Enable navigation blocking
4088
+ * Navigation Processor
4089
+ * Continuous guard — blocks all navigation attempts within the iframe.
5564
4090
  */
4091
+ const logger$6 = createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' });
4092
+ // ─── State ────────────────────────────────────────────────────────────────────
4093
+ let isEnabled = false;
4094
+ let showMessage = false;
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.');
4101
+ return;
4102
+ }
4103
+ if (!iframe.contentDocument || !iframe.contentWindow) {
4104
+ throw new Error('Iframe document or window not accessible');
4105
+ }
4106
+ const doc = iframe.contentDocument;
4107
+ const win = iframe.contentWindow;
4108
+ const originalOpen = win.open.bind(win);
4109
+ logger$6.configure({ enabled: !!cfg?.debug });
4110
+ 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),
4116
+ setupDOMMonitor(doc),
4117
+ ];
4118
+ attach$1(cfg?.debug);
4119
+ running$4 = true;
4120
+ logger$6.log('Navigation blocker started');
4121
+ }
4122
+ function stop$5() {
4123
+ if (!running$4)
4124
+ return;
4125
+ cleanups.forEach((fn) => fn());
4126
+ cleanups = [];
4127
+ detach$1();
4128
+ isEnabled = false;
4129
+ showMessage = false;
4130
+ running$4 = false;
4131
+ logger$6.log('Navigation blocker stopped');
4132
+ }
5565
4133
  function enable() {
5566
4134
  if (!running$4) {
5567
- logger$3.warn('Blocker is not running. Call start() first.');
4135
+ logger$6.warn('Blocker is not running. Call start() first.');
5568
4136
  return;
5569
4137
  }
5570
4138
  isEnabled = true;
5571
- logger$3.log('Navigation blocking enabled');
4139
+ logger$6.log('Navigation blocking enabled');
4140
+ }
4141
+
4142
+ const registry$1 = [];
4143
+ /**
4144
+ * Register a global fix.
4145
+ * Fixes are run in registration order.
4146
+ */
4147
+ function register$1(fix) {
4148
+ registry$1.push(fix);
4149
+ }
4150
+ /**
4151
+ * Returns all fixes that are active for the given context.
4152
+ * A fix is active if it has no `shouldApply`, or `shouldApply` returns true.
4153
+ */
4154
+ function getActiveFixes(ctx) {
4155
+ return registry$1.filter((fix) => {
4156
+ try {
4157
+ return !fix.shouldApply || fix.shouldApply(ctx);
4158
+ }
4159
+ catch {
4160
+ return false;
4161
+ }
4162
+ });
5572
4163
  }
5573
4164
 
5574
4165
  /**
@@ -5576,7 +4167,7 @@ function enable() {
5576
4167
  * Enforces computed CSS styles with viewport unit verification
5577
4168
  * @module computed-style-enforcer
5578
4169
  */
5579
- const logger$2 = createLogger({
4170
+ const logger$5 = createLogger({
5580
4171
  enabled: false,
5581
4172
  prefix: 'ComputedStyleEnforcer',
5582
4173
  });
@@ -5584,7 +4175,7 @@ const logger$2 = createLogger({
5584
4175
  // Constants
5585
4176
  // ============================================================================
5586
4177
  const DEFAULT_TOLERANCE_PX = 5;
5587
- const VIEWPORT_UNIT_REGEX$1 = /([-.\d]+)(vh|svh|lvh|dvh|%)/gi;
4178
+ const VIEWPORT_UNIT_REGEX = /([-.\d]+)(vh|svh|lvh|dvh|%)/gi;
5588
4179
  const DEFAULT_CSS_VALUES = ['none', 'auto', 'normal', '0px'];
5589
4180
  const CRITICAL_PROPERTIES = [
5590
4181
  'display',
@@ -5636,7 +4227,7 @@ let running$3 = false;
5636
4227
  /**
5637
4228
  * Get viewport unit map for conversion from current state
5638
4229
  */
5639
- function getViewportUnitMap$1() {
4230
+ function getViewportUnitMap() {
5640
4231
  if (!config$2) {
5641
4232
  throw new Error('Config is not initialized');
5642
4233
  }
@@ -5654,7 +4245,7 @@ function getViewportUnitMap$1() {
5654
4245
  /**
5655
4246
  * Calculate expected pixel value from viewport unit using current state
5656
4247
  */
5657
- function calculateExpectedPx$1(value, unit) {
4248
+ function calculateExpectedPx(value, unit) {
5658
4249
  if (!config$2) {
5659
4250
  throw new Error('Config is not initialized');
5660
4251
  }
@@ -5662,7 +4253,7 @@ function calculateExpectedPx$1(value, unit) {
5662
4253
  if (unitLower === '%') {
5663
4254
  return (value / 100) * config$2.targetHeight;
5664
4255
  }
5665
- const unitMap = getViewportUnitMap$1();
4256
+ const unitMap = getViewportUnitMap();
5666
4257
  return (value / 100) * (unitMap[unitLower] || 0);
5667
4258
  }
5668
4259
  /**
@@ -5686,7 +4277,7 @@ function shouldReplaceValue(computedValue, originalValue, tolerance = DEFAULT_TO
5686
4277
  return false; // Cannot verify, don't replace
5687
4278
  }
5688
4279
  // Parse original value to check what it should be
5689
- const regex = new RegExp(VIEWPORT_UNIT_REGEX$1.source, VIEWPORT_UNIT_REGEX$1.flags);
4280
+ const regex = new RegExp(VIEWPORT_UNIT_REGEX.source, VIEWPORT_UNIT_REGEX.flags);
5690
4281
  const match = originalValue.match(regex);
5691
4282
  if (!match) {
5692
4283
  return false; // No viewport units found, don't replace
@@ -5697,16 +4288,16 @@ function shouldReplaceValue(computedValue, originalValue, tolerance = DEFAULT_TO
5697
4288
  return false;
5698
4289
  }
5699
4290
  // Calculate expected value based on unit and target config
5700
- const expectedPx = calculateExpectedPx$1(num, unit);
4291
+ const expectedPx = calculateExpectedPx(num, unit);
5701
4292
  // Check if computed value matches expected value (within tolerance)
5702
4293
  const diff = Math.abs(computedPx - expectedPx);
5703
4294
  if (diff <= tolerance) {
5704
4295
  // Matches target config, OK to replace
5705
- logger$2.log(`OK to replace: computed=${computedValue} matches expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
4296
+ logger$5.log(`OK to replace: computed=${computedValue} matches expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
5706
4297
  return true;
5707
4298
  }
5708
4299
  // Different from target config, DON'T replace - value already has correct computation
5709
- logger$2.log(`Skip replace: computed=${computedValue} differs from expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px) - keeping current value`);
4300
+ logger$5.log(`Skip replace: computed=${computedValue} differs from expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px) - keeping current value`);
5710
4301
  return false;
5711
4302
  }
5712
4303
  /**
@@ -5734,15 +4325,15 @@ function applyPropertyWithVerification(element, prop, computedValue, originalVal
5734
4325
  */
5735
4326
  function start$4(d, w, cfg, options = {}) {
5736
4327
  if (running$3) {
5737
- logger$2.warn('Enforcer is already running. Call stop() first.');
4328
+ logger$5.warn('Enforcer is already running. Call stop() first.');
5738
4329
  return;
5739
4330
  }
5740
4331
  doc$1 = d;
5741
4332
  win$1 = w;
5742
4333
  config$2 = cfg;
5743
4334
  running$3 = true;
5744
- logger$2.configure({ enabled: !!options.debug });
5745
- logger$2.log('Computed style enforcer started');
4335
+ logger$5.configure({ enabled: !!options.debug });
4336
+ logger$5.log('Computed style enforcer started');
5746
4337
  }
5747
4338
  /**
5748
4339
  * Stop the enforcer and clear state
@@ -5757,7 +4348,7 @@ function stop$4() {
5757
4348
  elementsWithViewportUnits$1.clear();
5758
4349
  originalValues$1 = new WeakMap();
5759
4350
  running$3 = false;
5760
- logger$2.log('Computed style enforcer stopped');
4351
+ logger$5.log('Computed style enforcer stopped');
5761
4352
  }
5762
4353
  /**
5763
4354
  * Track an element with viewport units
@@ -5765,7 +4356,7 @@ function stop$4() {
5765
4356
  */
5766
4357
  function trackElement(element, propertyOriginalValues) {
5767
4358
  if (!running$3) {
5768
- logger$2.warn('Enforcer is not running. Call start() first.');
4359
+ logger$5.warn('Enforcer is not running. Call start() first.');
5769
4360
  return;
5770
4361
  }
5771
4362
  elementsWithViewportUnits$1.add(element);
@@ -5787,7 +4378,7 @@ function trackElement(element, propertyOriginalValues) {
5787
4378
  */
5788
4379
  function processElement(element, options = {}) {
5789
4380
  if (!running$3 || !doc$1 || !win$1 || !config$2) {
5790
- logger$2.warn('Enforcer is not running. Call start() first.');
4381
+ logger$5.warn('Enforcer is not running. Call start() first.');
5791
4382
  return 0;
5792
4383
  }
5793
4384
  if (!elementsWithViewportUnits$1.has(element)) {
@@ -5816,324 +4407,188 @@ function processElement(element, options = {}) {
5816
4407
  /**
5817
4408
  * Process all tracked elements
5818
4409
  * Enforce computed styles to inline for all elements with viewport units
5819
- */
5820
- function processAll(options = {}) {
5821
- if (!running$3 || !doc$1 || !win$1 || !config$2) {
5822
- logger$2.warn('Enforcer is not running. Call start() first.');
5823
- return 0;
5824
- }
5825
- let totalCount = 0;
5826
- elementsWithViewportUnits$1.forEach((element) => {
5827
- totalCount += processElement(element, options);
5828
- });
5829
- logger$2.log(`Enforced ${totalCount} computed styles for ${elementsWithViewportUnits$1.size} elements`);
5830
- return totalCount;
5831
- }
5832
-
5833
- /**
5834
- * Viewport Unit Replacer Module
5835
- * Replaces viewport units (vh, vw, %) with pixel values in iframe
5836
- * @module iframe-viewport-replacer
5837
- */
5838
- const logger$1 = createLogger({
5839
- enabled: false,
5840
- prefix: 'ViewportReplacer',
5841
- });
5842
- // ============================================================================
5843
- // Constants
5844
- // ============================================================================
5845
- const VIEWPORT_UNIT_REGEX = /([-.\d]+)(vh|svh|lvh|dvh|%)/gi;
5846
- const HEIGHT_RELATED_PROPERTIES = ['height', 'min-height', 'max-height', 'top', 'bottom'];
5847
- // ============================================================================
5848
- // State
5849
- // ============================================================================
5850
- let doc = null;
5851
- let win = null;
5852
- let config$1 = null;
5853
- const regex = new RegExp(VIEWPORT_UNIT_REGEX.source, VIEWPORT_UNIT_REGEX.flags);
5854
- const elementsWithViewportUnits = new Set();
5855
- let originalValues = new WeakMap();
5856
- let running$2 = false;
5857
- function start$3(iframe, cfg) {
5858
- if (running$2) {
5859
- logger$1.warn('Replacer is already running. Call stop() first.');
5860
- return;
5861
- }
5862
- if (!iframe.contentDocument || !iframe.contentWindow) {
5863
- throw new Error('Iframe document or window not accessible');
5864
- }
5865
- doc = iframe.contentDocument;
5866
- win = iframe.contentWindow;
5867
- config$1 = cfg;
5868
- running$2 = true;
5869
- logger$1.configure({ enabled: !!cfg?.debug });
5870
- // Initialize enforcer
5871
- start$4(doc, win, cfg, { debug: !!cfg?.debug });
5872
- logger$1.log('Viewport replacer started');
5873
- }
5874
- function stop$3() {
5875
- if (!running$2) {
5876
- return;
5877
- }
5878
- // Stop enforcer
5879
- stop$4();
5880
- doc = null;
5881
- win = null;
5882
- config$1 = null;
5883
- elementsWithViewportUnits.clear();
5884
- originalValues = new WeakMap();
5885
- running$2 = false;
5886
- logger$1.log('Viewport replacer stopped');
5887
- }
5888
- async function run() {
5889
- if (!running$2 || !doc) {
5890
- logger$1.warn('Replacer is not running. Call start() first.');
5891
- return { height: 1000, width: 1000 };
5892
- }
5893
- try {
5894
- logger$1.log('Starting viewport units replacement...');
5895
- processInlineStyles();
5896
- processStyleTags();
5897
- processStylesheets();
5898
- await processLinkedStylesheets();
5899
- // Wait for stylesheets to be fully applied
5900
- await new Promise((resolve) => requestAnimationFrame(resolve));
5901
- // Enforce computed styles to inline with !important
5902
- enforceComputedStylesToInline();
5903
- // Setup MutationObserver to handle dynamically added elements (e.g., dialogs)
5904
- // setupMutationObserver();
5905
- return await new Promise((resolve) => {
5906
- requestAnimationFrame(() => {
5907
- const height = getFinalHeight();
5908
- const width = getFinalWidth();
5909
- logger$1.log('Calculated dimensions:', { height, width });
5910
- resolve({ height, width });
5911
- });
5912
- });
5913
- }
5914
- catch (err) {
5915
- logger$1.error('Critical error:', err);
5916
- return {
5917
- height: doc.body?.scrollHeight || 1000,
5918
- width: doc.body?.scrollWidth || 1000,
5919
- };
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.');
4414
+ return 0;
5920
4415
  }
4416
+ let totalCount = 0;
4417
+ elementsWithViewportUnits$1.forEach((element) => {
4418
+ totalCount += processElement(element, options);
4419
+ });
4420
+ logger$5.log(`Enforced ${totalCount} computed styles for ${elementsWithViewportUnits$1.size} elements`);
4421
+ return totalCount;
5921
4422
  }
5922
- // ============================================================================
5923
- // Helper Functions
5924
- // ============================================================================
4423
+
4424
+ /**
4425
+ * Core viewport unit replacement logic.
4426
+ * Converts vh/vw/svh/dvh/% to pixel values across all CSS in the iframe.
4427
+ */
4428
+ const logger$4 = createLogger({ enabled: false, prefix: 'ViewportUnitReplacer' });
4429
+ // ─── Constants ────────────────────────────────────────────────────────────────
4430
+ const HEIGHT_RELATED_PROPERTIES = ['height', 'min-height', 'max-height', 'top', 'bottom'];
4431
+ // ─── Per-run tracking state (reset on each process() call) ───────────────────
4432
+ let elementsWithViewportUnits = new Set();
4433
+ let originalValues = new WeakMap();
4434
+ // ─── Regex ────────────────────────────────────────────────────────────────────
4435
+ /** Fresh instance every call — avoids shared lastIndex state with the g flag. */
4436
+ function createRegex() {
4437
+ return /([-.\\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/gi;
4438
+ }
4439
+ // ─── Unit conversion ─────────────────────────────────────────────────────────
5925
4440
  function px(value) {
5926
4441
  return `${value.toFixed(2)}px`;
5927
4442
  }
5928
- function getViewportUnitMap() {
5929
- if (!config$1) {
5930
- throw new Error('Config is not initialized');
5931
- }
4443
+ function getUnitMap(ctx) {
5932
4444
  return {
5933
- vh: config$1.targetHeight,
5934
- svh: config$1.targetHeight,
5935
- lvh: config$1.targetHeight,
5936
- dvh: config$1.targetHeight,
5937
- vw: config$1.targetWidth,
5938
- svw: config$1.targetWidth,
5939
- lvw: config$1.targetWidth,
5940
- dvw: config$1.targetWidth,
4445
+ vh: ctx.targetHeight,
4446
+ svh: ctx.targetHeight,
4447
+ lvh: ctx.targetHeight,
4448
+ dvh: ctx.targetHeight,
4449
+ vw: ctx.targetWidth,
4450
+ svw: ctx.targetWidth,
4451
+ lvw: ctx.targetWidth,
4452
+ dvw: ctx.targetWidth,
5941
4453
  };
5942
4454
  }
5943
- function calculateExpectedPx(value, unit) {
5944
- if (!config$1) {
5945
- throw new Error('Config is not initialized');
5946
- }
5947
- const unitLower = unit.toLowerCase();
5948
- if (unitLower === '%') {
5949
- return (value / 100) * config$1.targetHeight;
5950
- }
5951
- const unitMap = getViewportUnitMap();
5952
- return (value / 100) * (unitMap[unitLower] || 0);
4455
+ function toPx(value, unit, ctx) {
4456
+ const u = unit.toLowerCase();
4457
+ if (u === '%')
4458
+ return (value / 100) * ctx.targetHeight;
4459
+ return (value / 100) * (getUnitMap(ctx)[u] ?? 0);
5953
4460
  }
5954
- function convert(value, unit) {
4461
+ function convert(value, unit, ctx) {
5955
4462
  const num = parseFloat(value);
5956
- if (isNaN(num))
5957
- return value;
5958
- return px(calculateExpectedPx(num, unit));
4463
+ return isNaN(num) ? value : px(toPx(num, unit, ctx));
5959
4464
  }
5960
- function isHeightRelatedProperty(propertyName) {
5961
- return HEIGHT_RELATED_PROPERTIES.includes(propertyName);
4465
+ function isHeightRelated(prop) {
4466
+ return HEIGHT_RELATED_PROPERTIES.includes(prop);
5962
4467
  }
5963
- function extractPropertyName(cssText, match) {
5964
- const beforeMatch = cssText.substring(0, cssText.indexOf(match));
5965
- const propertyMatch = beforeMatch.match(/([a-z-]+)\s*:\s*$/i);
5966
- return propertyMatch ? propertyMatch[1].toLowerCase() : '';
4468
+ /**
4469
+ * Use `matchOffset` (from replace() callback) instead of indexOf to get the
4470
+ * exact position of the current match — avoids false matches for duplicate values.
4471
+ */
4472
+ function extractProperty(cssText, matchOffset) {
4473
+ const before = cssText.substring(0, matchOffset);
4474
+ const m = before.match(/([a-z-]+)\s*:\s*[^;{}]*$/i);
4475
+ return m ? m[1].toLowerCase() : '';
5967
4476
  }
5968
- function replaceInText(cssText) {
5969
- return cssText.replace(regex, (match, value, unit) => {
5970
- // For percentage, only convert if it's a height-related property
4477
+ function replaceInText(cssText, ctx) {
4478
+ return cssText.replace(createRegex(), (match, value, unit, offset) => {
5971
4479
  if (unit === '%') {
5972
- const propertyName = extractPropertyName(cssText, match);
5973
- // Only convert percentage for height-related properties
5974
- if (isHeightRelatedProperty(propertyName)) {
5975
- return convert(value, unit);
5976
- }
5977
- // For width or other properties, keep original
5978
- return match;
4480
+ return isHeightRelated(extractProperty(cssText, offset)) ? convert(value, unit, ctx) : match;
5979
4481
  }
5980
- return convert(value, unit);
4482
+ return convert(value, unit, ctx);
5981
4483
  });
5982
4484
  }
5983
- function trackOriginalValuesForSelector(selector, propertyOriginalValues) {
5984
- if (!doc)
5985
- return;
4485
+ // ─── Element tracking ─────────────────────────────────────────────────────────
4486
+ function trackSelector(selector, propOriginals, ctx) {
5986
4487
  try {
5987
- const elements = doc.querySelectorAll(selector);
5988
- elements.forEach((el) => {
4488
+ ctx.doc.querySelectorAll(selector).forEach((el) => {
5989
4489
  elementsWithViewportUnits.add(el);
5990
- // Store original values for this element
5991
- let elementOriginals = originalValues.get(el);
5992
- if (!elementOriginals) {
5993
- elementOriginals = new Map();
5994
- originalValues.set(el, elementOriginals);
4490
+ let originals = originalValues.get(el);
4491
+ if (!originals) {
4492
+ originals = new Map();
4493
+ originalValues.set(el, originals);
5995
4494
  }
5996
- // Merge property original values (don't override existing)
5997
- propertyOriginalValues.forEach((value, prop) => {
5998
- if (!elementOriginals.has(prop)) {
5999
- elementOriginals.set(prop, value);
6000
- }
4495
+ propOriginals.forEach((v, k) => {
4496
+ if (!originals.has(k))
4497
+ originals.set(k, v);
6001
4498
  });
6002
- // Track in enforcer
6003
- trackElement(el, propertyOriginalValues);
4499
+ trackElement(el, propOriginals);
6004
4500
  });
6005
4501
  }
6006
- catch (error) {
6007
- // Invalid selector, skip with warning
6008
- logger$1.warn('Invalid selector, skipping:', selector, error);
4502
+ catch {
4503
+ logger$4.warn('Invalid selector, skipping:', selector);
6009
4504
  }
6010
4505
  }
6011
- function triggerReflow() {
6012
- if (!doc)
6013
- return;
6014
- void doc.body.offsetHeight;
6015
- }
6016
- function getFinalHeight() {
6017
- if (!doc)
6018
- return 1000;
6019
- triggerReflow();
6020
- return Math.max(doc.body?.scrollHeight || 0, doc.body?.offsetHeight || 0, doc.documentElement?.scrollHeight || 0, doc.documentElement?.offsetHeight || 0, doc.documentElement?.clientHeight || 0);
6021
- }
6022
- function getFinalWidth() {
6023
- if (!doc)
6024
- return 1000;
6025
- return Math.max(doc.body?.scrollWidth || 0, doc.body?.offsetWidth || 0, doc.documentElement?.scrollWidth || 0, doc.documentElement?.offsetWidth || 0, doc.documentElement?.clientWidth || 0);
6026
- }
6027
- // ============================================================================
6028
- // Processing Functions
6029
- // ============================================================================
6030
- function processInlineStyles() {
6031
- if (!doc)
6032
- return 0;
4506
+ // ─── CSS processing ───────────────────────────────────────────────────────────
4507
+ function processInlineStyles(ctx) {
6033
4508
  let count = 0;
6034
- doc.querySelectorAll('[style]').forEach((el) => {
4509
+ ctx.doc.querySelectorAll('[style]').forEach((el) => {
6035
4510
  const style = el.getAttribute('style');
6036
- if (style && regex.test(style)) {
6037
- regex.lastIndex = 0;
6038
- // Track this element before replacing
4511
+ if (style && createRegex().test(style)) {
6039
4512
  elementsWithViewportUnits.add(el);
6040
- el.setAttribute('style', replaceInText(style));
4513
+ el.setAttribute('style', replaceInText(style, ctx));
6041
4514
  count++;
6042
4515
  }
6043
4516
  });
6044
- logger$1.log(`Replaced ${count} inline style elements`);
4517
+ logger$4.log(`Replaced ${count} inline style elements`);
6045
4518
  return count;
6046
4519
  }
6047
- function processStyleTags() {
6048
- if (!doc)
6049
- return 0;
4520
+ function processStyleTags(ctx) {
6050
4521
  let count = 0;
6051
- doc.querySelectorAll('style').forEach((tag) => {
4522
+ ctx.doc.querySelectorAll('style').forEach((tag) => {
6052
4523
  const css = tag.textContent || '';
6053
- if (regex.test(css)) {
6054
- regex.lastIndex = 0;
6055
- tag.textContent = replaceInText(css);
4524
+ if (createRegex().test(css)) {
4525
+ tag.textContent = replaceInText(css, ctx);
6056
4526
  count++;
6057
4527
  }
6058
4528
  });
6059
- logger$1.log(`Replaced ${count} <style> tags`);
4529
+ logger$4.log(`Replaced ${count} <style> tags`);
6060
4530
  return count;
6061
4531
  }
6062
- function processRule(rule) {
4532
+ function processRule(rule, ctx) {
6063
4533
  let count = 0;
6064
4534
  if ('style' in rule && rule.style) {
6065
4535
  const cssRule = rule;
6066
4536
  const style = cssRule.style;
6067
- let hasViewportUnits = false;
6068
- const propertyOriginalValues = new Map();
4537
+ let hasVp = false;
4538
+ const propOriginals = new Map();
6069
4539
  for (let i = 0; i < style.length; i++) {
6070
4540
  const prop = style[i];
6071
4541
  const value = style.getPropertyValue(prop);
6072
- if (value && regex.test(value)) {
6073
- regex.lastIndex = 0;
6074
- hasViewportUnits = true;
6075
- // Store original value before replacement
6076
- propertyOriginalValues.set(prop, value);
6077
- style.setProperty(prop, replaceInText(value), style.getPropertyPriority(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));
6078
4546
  count++;
6079
4547
  }
6080
4548
  }
6081
- // Track elements matching this rule if it has viewport units
6082
- if (hasViewportUnits && cssRule.selectorText) {
6083
- trackOriginalValuesForSelector(cssRule.selectorText, propertyOriginalValues);
6084
- }
4549
+ if (hasVp && cssRule.selectorText)
4550
+ trackSelector(cssRule.selectorText, propOriginals, ctx);
6085
4551
  }
6086
4552
  if ('cssRules' in rule) {
6087
- const rules = rule;
6088
- for (const r of Array.from(rules.cssRules || [])) {
6089
- count += processRule(r);
4553
+ for (const r of Array.from(rule.cssRules || [])) {
4554
+ count += processRule(r, ctx);
6090
4555
  }
6091
4556
  }
6092
4557
  return count;
6093
4558
  }
6094
- function processStylesheets() {
6095
- if (!doc || !win)
6096
- return 0;
6097
- const windowOrigin = win.location.origin;
4559
+ /** Processes only inline <style> sheets. Linked sheets are handled by processLinkedStylesheets. */
4560
+ function processStylesheets(ctx) {
6098
4561
  let total = 0;
6099
- Array.from(doc.styleSheets).forEach((sheet) => {
4562
+ Array.from(ctx.doc.styleSheets).forEach((sheet) => {
4563
+ if (sheet.href)
4564
+ return; // deferred to processLinkedStylesheets
6100
4565
  try {
6101
- // Bỏ qua external CSS (cross-origin)
6102
- if (sheet.href && !sheet.href.startsWith(windowOrigin)) {
6103
- logger$1.log('Skipping external CSS:', sheet.href);
6104
- return;
6105
- }
6106
- const rules = sheet.cssRules || sheet.rules;
6107
- if (!rules)
6108
- return;
6109
- for (const rule of Array.from(rules)) {
6110
- total += processRule(rule);
4566
+ for (const rule of Array.from(sheet.cssRules || [])) {
4567
+ total += processRule(rule, ctx);
6111
4568
  }
6112
4569
  }
6113
4570
  catch (e) {
6114
- logger$1.warn('Cannot read stylesheet (CORS?):', e.message);
4571
+ logger$4.warn('Cannot read stylesheet (CORS?):', e.message);
6115
4572
  }
6116
4573
  });
6117
- logger$1.log(`Replaced ${total} rules in stylesheets`);
4574
+ logger$4.log(`Replaced ${total} rules in inline stylesheets`);
6118
4575
  return total;
6119
4576
  }
6120
- async function processLinkedStylesheets() {
6121
- if (!doc || !win)
6122
- return 0;
6123
- const links = doc.querySelectorAll('link[rel="stylesheet"]');
4577
+ async function processLinkedStylesheets(ctx) {
4578
+ const links = ctx.doc.querySelectorAll('link[rel="stylesheet"]');
6124
4579
  let count = 0;
6125
4580
  for (const link of Array.from(links)) {
6126
- if (!link.href.startsWith(win.location.origin)) {
6127
- logger$1.log('Skipping external CSS:', link.href);
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);
6128
4584
  continue;
6129
4585
  }
6130
4586
  try {
6131
4587
  const res = await fetch(link.href);
6132
4588
  let css = await res.text();
6133
- if (regex.test(css)) {
6134
- regex.lastIndex = 0;
6135
- css = replaceInText(css);
6136
- const style = doc.createElement('style');
4589
+ if (createRegex().test(css)) {
4590
+ css = replaceInText(css, ctx);
4591
+ const style = ctx.doc.createElement('style');
6137
4592
  style.textContent = css;
6138
4593
  style.dataset.originalHref = link.href;
6139
4594
  link.parentNode?.insertBefore(style, link);
@@ -6142,17 +4597,453 @@ async function processLinkedStylesheets() {
6142
4597
  }
6143
4598
  }
6144
4599
  catch (e) {
6145
- logger$1.warn('Cannot load CSS:', link.href, e);
4600
+ logger$4.warn('Cannot load CSS:', link.href, e);
6146
4601
  }
6147
4602
  }
6148
- logger$1.log(`Replaced ${count} linked CSS files`);
4603
+ logger$4.log(`Replaced ${count} linked CSS files`);
6149
4604
  return count;
6150
4605
  }
6151
- function enforceComputedStylesToInline() {
6152
- // Use enforcer to process all tracked elements
6153
- const count = processAll({ debug: !!config$1?.debug });
6154
- logger$1.log(`Enforced ${count} computed styles to inline with !important for ${elementsWithViewportUnits.size} elements`);
6155
- return count;
4606
+ // ─── Public entry point ───────────────────────────────────────────────────────
4607
+ async function processViewportUnits(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: processViewportUnits,
4631
+ });
4632
+
4633
+ /**
4634
+ * GemPages Swiper Fix
4635
+ *
4636
+ * Issue: GemPages Swiper slides use `height: 100vh` internally. After viewport
4637
+ * unit conversion, inactive slides retain their converted height but are not
4638
+ * positioned correctly, causing the page height to be over-calculated.
4639
+ *
4640
+ * Fix:
4641
+ * - beforeProcess: lock swiper containers to a fixed height so conversion
4642
+ * doesn't affect their layout.
4643
+ * - afterProcess: ensure only the active slide is visible and the swiper
4644
+ * wrapper height matches the container.
4645
+ */
4646
+ const SWIPER_CONTAINER_SELECTOR = '.gp-swiper, [data-gp-element="slider"]';
4647
+ const SWIPER_WRAPPER_SELECTOR = '.swiper-wrapper';
4648
+ const SWIPER_SLIDE_SELECTOR = '.swiper-slide';
4649
+ const SWIPER_SLIDE_ACTIVE_SELECTOR = '.swiper-slide-active';
4650
+ const lockedSwipers = [];
4651
+ function lockSwiperHeights({ doc, targetHeight }) {
4652
+ lockedSwipers.length = 0;
4653
+ doc.querySelectorAll(SWIPER_CONTAINER_SELECTOR).forEach((container) => {
4654
+ lockedSwipers.push({ container, originalHeight: container.style.height });
4655
+ container.style.setProperty('height', `${targetHeight}px`, 'important');
4656
+ });
4657
+ }
4658
+ function restoreSwiperAndFixLayout({ doc }) {
4659
+ // Restore original heights
4660
+ lockedSwipers.forEach(({ container, originalHeight }) => {
4661
+ container.style.removeProperty('height');
4662
+ if (originalHeight)
4663
+ container.style.height = originalHeight;
4664
+ });
4665
+ lockedSwipers.length = 0;
4666
+ // Ensure inactive slides don't bleed into layout
4667
+ doc.querySelectorAll(SWIPER_CONTAINER_SELECTOR).forEach((container) => {
4668
+ const wrapper = container.querySelector(SWIPER_WRAPPER_SELECTOR);
4669
+ const activeSlide = container.querySelector(SWIPER_SLIDE_ACTIVE_SELECTOR);
4670
+ const allSlides = container.querySelectorAll(SWIPER_SLIDE_SELECTOR);
4671
+ // Hide all inactive slides from layout flow
4672
+ allSlides.forEach((slide) => {
4673
+ if (slide !== activeSlide) {
4674
+ slide.style.setProperty('visibility', 'hidden', 'important');
4675
+ slide.style.setProperty('position', 'absolute', 'important');
4676
+ }
4677
+ });
4678
+ // Sync wrapper height to active slide
4679
+ if (wrapper && activeSlide) {
4680
+ wrapper.style.setProperty('height', `${activeSlide.offsetHeight}px`, 'important');
4681
+ }
4682
+ });
4683
+ }
4684
+
4685
+ register$1({
4686
+ name: 'gempages-swiper',
4687
+ description: 'GemPages Swiper slides use 100vh causing layout height inflation',
4688
+ shouldApply: ({ doc }) => !!doc.querySelector('.gp-swiper, [data-gp-element="slider"]'),
4689
+ extraIgnoreSelectors: ['.swiper-slide:not(.swiper-slide-active)'],
4690
+ beforeProcess: lockSwiperHeights,
4691
+ afterProcess: restoreSwiperAndFixLayout,
4692
+ });
4693
+
4694
+ /**
4695
+ * GemPages v7 Slider Fix
4696
+ *
4697
+ * At DOM snapshot time the slider library has already computed pixel values on each
4698
+ * `.gem-slider-item` based on the original viewport width:
4699
+ * - `min-width` / `max-width` are fixed px (e.g. 2288px or 278px)
4700
+ * - `transform: translate3d(X, 0, 0)` offsets are derived from those widths
4701
+ *
4702
+ * Since we capture HTML+CSS without scripts, the library cannot re-initialize,
4703
+ * so we fix the widths and rescale the transforms manually.
4704
+ *
4705
+ * Two slider variants:
4706
+ *
4707
+ * 1. Full-width (1 item per view):
4708
+ * min-width = originalViewportWidth → newItemWidth = targetWidth
4709
+ *
4710
+ * 2. Multi-item (N items per view):
4711
+ * CSS var `--minw: calc(100% / N - Gpx)` encodes the formula.
4712
+ * newItemWidth = targetWidth / N - G
4713
+ *
4714
+ * In both cases: scale = newItemWidth / originalItemWidth
4715
+ * Transforms are rescaled proportionally (only when X ≠ 0).
4716
+ */
4717
+ const SLIDER_ITEM_SELECTOR = '.gem-slider-item';
4718
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
4719
+ function parseTranslate3d(transform) {
4720
+ const match = transform.match(/translate3d\(\s*([-\d.]+)px\s*,\s*([-\d.]+)px\s*,\s*([-\d.]+)px\s*\)/);
4721
+ if (!match)
4722
+ return null;
4723
+ return { x: parseFloat(match[1]), y: parseFloat(match[2]), z: parseFloat(match[3]) };
4724
+ }
4725
+ /**
4726
+ * Parse `calc(100% / N - Gpx)` and evaluate against containerWidth.
4727
+ * Returns null if the format doesn't match.
4728
+ */
4729
+ function parseCalcFraction(cssValue, containerWidth) {
4730
+ const match = cssValue.match(/calc\(\s*100%\s*\/\s*([\d.]+)\s*-\s*([\d.]+)px\s*\)/);
4731
+ if (!match)
4732
+ return null;
4733
+ const itemsPerRow = parseFloat(match[1]);
4734
+ const gap = parseFloat(match[2]);
4735
+ if (!itemsPerRow)
4736
+ return null;
4737
+ return containerWidth / itemsPerRow - gap;
4738
+ }
4739
+ /**
4740
+ * Determine the correct item width at the new targetWidth.
4741
+ *
4742
+ * Priority:
4743
+ * 1. `--minw` CSS custom property → extract formula and re-evaluate
4744
+ * 2. `--maxw` CSS custom property → same
4745
+ * 3. Fallback: targetWidth (full-width single-item slider)
4746
+ */
4747
+ function resolveNewItemWidth(item, targetWidth) {
4748
+ for (const varName of ['--minw', '--maxw']) {
4749
+ const cssVar = item.style.getPropertyValue(varName).trim();
4750
+ if (!cssVar)
4751
+ continue;
4752
+ const calculated = parseCalcFraction(cssVar, targetWidth);
4753
+ if (calculated !== null && calculated > 0)
4754
+ return calculated;
4755
+ }
4756
+ return targetWidth;
4757
+ }
4758
+ function getDeviceType(deviceType) {
4759
+ switch (deviceType) {
4760
+ case EDeviceType.Mobile:
4761
+ return '-mobile';
4762
+ case EDeviceType.Tablet:
4763
+ return '-tablet';
4764
+ default:
4765
+ return '';
4766
+ }
4767
+ }
4768
+ function getMinWByDeviceType(deviceType) {
4769
+ return `--minw${getDeviceType(deviceType)}`;
4770
+ }
4771
+ function getMaxWByDeviceType(deviceType) {
4772
+ return `--maxw${getDeviceType(deviceType)}`;
4773
+ }
4774
+ // ─── Main fix ─────────────────────────────────────────────────────────────────
4775
+ function rescaleSliderItems({ doc, targetWidth, deviceType }) {
4776
+ doc.querySelectorAll(SLIDER_ITEM_SELECTOR).forEach((item) => {
4777
+ const originalWidth = parseFloat(item.style.minWidth) || parseFloat(item.style.maxWidth) || 0;
4778
+ if (!originalWidth)
4779
+ return;
4780
+ const newItemWidth = resolveNewItemWidth(item, targetWidth);
4781
+ if (newItemWidth === originalWidth)
4782
+ return;
4783
+ const minWVarName = getMinWByDeviceType(deviceType);
4784
+ const maxWVarName = getMaxWByDeviceType(deviceType);
4785
+ const newMinWidthByVar = `var(${minWVarName}, ${newItemWidth.toFixed(2)}px)`;
4786
+ const newMaxWidthByVar = `var(${maxWVarName}, ${newItemWidth.toFixed(2)}px)`;
4787
+ item.style.setProperty('min-width', newMinWidthByVar, 'important');
4788
+ item.style.setProperty('max-width', newMaxWidthByVar, 'important');
4789
+ const translate = parseTranslate3d(item.style.transform);
4790
+ const translateX = 0 - (translate?.x ?? 0);
4791
+ if (translate && translateX !== 0 && translateX > targetWidth) {
4792
+ const newX = `calc(1px - ${newMinWidthByVar})`;
4793
+ item.style.setProperty('transform', `translate3d(${newX}, ${translate.y}px, ${translate.z}px)`, 'important');
4794
+ }
4795
+ });
4796
+ }
4797
+
4798
+ register$1({
4799
+ name: 'gp-v7-slider',
4800
+ description: 'GemPages v7 slider: rescale .gem-slider-item min/max-width and translate3d X to match targetWidth',
4801
+ shouldApply: ({ doc }) => !!doc.querySelector('.gem-slider-item'),
4802
+ afterProcess: rescaleSliderItems,
4803
+ });
4804
+
4805
+ const logger$3 = createLogger({ enabled: false, prefix: 'ViewportReplacer' });
4806
+ let dimensionsListener = null;
4807
+ function attach(debug) {
4808
+ logger$3.configure({ enabled: !!debug });
4809
+ dimensionsListener = (e) => {
4810
+ const ev = e;
4811
+ logger$3.log('Dimensions applied:', ev.detail);
4812
+ };
4813
+ window.addEventListener('iframe-dimensions-applied', dimensionsListener);
4814
+ }
4815
+ function detach() {
4816
+ if (dimensionsListener) {
4817
+ window.removeEventListener('iframe-dimensions-applied', dimensionsListener);
4818
+ dimensionsListener = null;
4819
+ }
4820
+ }
4821
+
4822
+ /**
4823
+ * Default iframe dimension calculation.
4824
+ * Used as fallback when no fix overrides getDimensions().
4825
+ */
4826
+ function getFinalHeight(doc, win) {
4827
+ void doc.body.offsetHeight; // trigger reflow
4828
+ const bodyMinHeight = parseFloat(win.getComputedStyle(doc.body).minHeight) || 0;
4829
+ const htmlMinHeight = parseFloat(win.getComputedStyle(doc.documentElement).minHeight) || 0;
4830
+ return Math.max(doc.body?.scrollHeight || 0, doc.body?.offsetHeight || 0, doc.documentElement?.scrollHeight || 0, doc.documentElement?.offsetHeight || 0, doc.documentElement?.clientHeight || 0, bodyMinHeight, htmlMinHeight);
4831
+ }
4832
+ function getFinalWidth(doc) {
4833
+ return Math.max(doc.body?.scrollWidth || 0, doc.body?.offsetWidth || 0, doc.documentElement?.scrollWidth || 0, doc.documentElement?.offsetWidth || 0, doc.documentElement?.clientWidth || 0);
4834
+ }
4835
+
4836
+ /**
4837
+ * Viewport fix pipeline runner.
4838
+ *
4839
+ * Executes the 4-phase pipeline for a given context:
4840
+ * Phase 1: beforeProcess (global → shop)
4841
+ * Phase 2: process (global fixes)
4842
+ * Phase 3: afterProcess (shop → global)
4843
+ * Phase 4: getDimensions (shop → global → default)
4844
+ */
4845
+ const logger$2 = createLogger({ enabled: false, prefix: 'ViewportReplacer' });
4846
+ function configure(debug) {
4847
+ logger$2.configure({ enabled: debug });
4848
+ }
4849
+ async function run$1(ctx, activeGlobal, shopFix) {
4850
+ console.log(`🚀 🐥 ~ run ~ ctx:`, ctx);
4851
+ // ── Phase 1: beforeProcess ────────────────────────────────────────────────
4852
+ for (const fix of activeGlobal) {
4853
+ if (fix.beforeProcess) {
4854
+ logger$2.log(`[beforeProcess] ${fix.name}`);
4855
+ await fix.beforeProcess(ctx);
4856
+ }
4857
+ }
4858
+ if (shopFix?.beforeProcess) {
4859
+ logger$2.log('[beforeProcess] shop');
4860
+ await shopFix.beforeProcess(ctx);
4861
+ }
4862
+ // ── Phase 2: process ──────────────────────────────────────────────────────
4863
+ for (const fix of activeGlobal) {
4864
+ if (fix.process) {
4865
+ logger$2.log(`[process] ${fix.name}`);
4866
+ await fix.process(ctx);
4867
+ }
4868
+ }
4869
+ // ── Phase 3: afterProcess ─────────────────────────────────────────────────
4870
+ if (shopFix?.afterProcess) {
4871
+ logger$2.log('[afterProcess] shop');
4872
+ await shopFix.afterProcess(ctx);
4873
+ }
4874
+ for (const fix of activeGlobal) {
4875
+ if (fix.afterProcess) {
4876
+ logger$2.log(`[afterProcess] ${fix.name}`);
4877
+ await fix.afterProcess(ctx);
4878
+ }
4879
+ }
4880
+ // ── Phase 4: getDimensions ────────────────────────────────────────────────
4881
+ return new Promise((resolve) => {
4882
+ requestAnimationFrame(() => {
4883
+ let dimensions = null;
4884
+ // Priority: shop → global → default
4885
+ if (shopFix?.getDimensions) {
4886
+ dimensions = shopFix.getDimensions(ctx);
4887
+ if (dimensions)
4888
+ logger$2.log('Dimensions from shop fix:', dimensions);
4889
+ }
4890
+ if (!dimensions) {
4891
+ for (const fix of activeGlobal) {
4892
+ if (fix.getDimensions) {
4893
+ dimensions = fix.getDimensions(ctx);
4894
+ if (dimensions) {
4895
+ logger$2.log(`Dimensions from global fix [${fix.name}]:`, dimensions);
4896
+ break;
4897
+ }
4898
+ }
4899
+ }
4900
+ }
4901
+ if (!dimensions) {
4902
+ dimensions = { height: getFinalHeight(ctx.doc, ctx.win), width: getFinalWidth(ctx.doc) };
4903
+ }
4904
+ logger$2.log('Final dimensions:', dimensions);
4905
+ resolve(dimensions);
4906
+ });
4907
+ });
4908
+ }
4909
+
4910
+ const registry = new Map();
4911
+ /**
4912
+ * Register a viewport fix for a specific shop.
4913
+ * Accepts a numeric shop ID or a myshopify domain string.
4914
+ *
4915
+ * @example
4916
+ * register(1234, { description: 'Fix for My Shop', extraIgnoreSelectors: ['.hero'] });
4917
+ * register('my-shop.myshopify.com', { afterProcess: ({ doc }) => { ... } });
4918
+ */
4919
+ function register(shopId, fix) {
4920
+ if (registry.has(shopId)) {
4921
+ console.warn(`[ViewportShopFixes] Fix for shop "${shopId}" already registered — overwriting.`);
4922
+ }
4923
+ registry.set(shopId, fix);
4924
+ }
4925
+ function get(shopId) {
4926
+ return registry.get(shopId) ?? null;
4927
+ }
4928
+
4929
+ /**
4930
+ * Shop 566240210141053597
4931
+ *
4932
+ * Known issues:
4933
+ * - The hero section uses `height: 100vh` with a sticky internal element that
4934
+ * overflows the layout after viewport unit conversion → clamp it manually.
4935
+ * - The mega-menu drawer holds `min-height: 100vh` which inflates the total
4936
+ * page height calculation → exclude it from dimension measurement.
4937
+ */
4938
+ const HERO_SELECTOR = '#shopify-section-hero';
4939
+ const MEGA_MENU_SELECTOR = '.mega-menu__drawer';
4940
+ // ─── beforeProcess ────────────────────────────────────────────────────────────
4941
+ /**
4942
+ * Hide the mega-menu drawer before processing so it doesn't affect layout.
4943
+ * It will be restored in afterProcess.
4944
+ */
4945
+ function hideMegaMenuDrawer({ doc }) {
4946
+ const drawer = doc.querySelector(MEGA_MENU_SELECTOR);
4947
+ if (!drawer)
4948
+ return;
4949
+ drawer.dataset.originalDisplay = drawer.style.display;
4950
+ drawer.style.setProperty('display', 'none', 'important');
4951
+ }
4952
+ // ─── afterProcess ─────────────────────────────────────────────────────────────
4953
+ /**
4954
+ * Restore mega-menu and clamp hero height to prevent overflow.
4955
+ */
4956
+ function restoreAndFixLayout({ doc, targetHeight }) {
4957
+ // Restore mega-menu
4958
+ const drawer = doc.querySelector(MEGA_MENU_SELECTOR);
4959
+ if (drawer) {
4960
+ const original = drawer.dataset.originalDisplay ?? '';
4961
+ drawer.style.removeProperty('display');
4962
+ if (original)
4963
+ drawer.style.display = original;
4964
+ delete drawer.dataset.originalDisplay;
4965
+ }
4966
+ // Clamp hero height to targetHeight
4967
+ const hero = doc.querySelector(HERO_SELECTOR);
4968
+ if (hero) {
4969
+ hero.style.setProperty('height', `${targetHeight}px`, 'important');
4970
+ hero.style.setProperty('max-height', `${targetHeight}px`, 'important');
4971
+ }
4972
+ }
4973
+
4974
+ register(`566240210141053597`, {
4975
+ description: 'Hero 100vh overflow + mega-menu drawer inflates page height',
4976
+ extraIgnoreSelectors: ['.mega-menu__drawer'],
4977
+ beforeProcess: hideMegaMenuDrawer,
4978
+ afterProcess: restoreAndFixLayout,
4979
+ });
4980
+
4981
+ const logger$1 = createLogger({ enabled: false, prefix: 'ViewportReplacer' });
4982
+ // ─── State ────────────────────────────────────────────────────────────────────
4983
+ let doc = null;
4984
+ let win = null;
4985
+ let config$1 = null;
4986
+ let shopFix = null;
4987
+ let running$2 = false;
4988
+ // ─── Public API ───────────────────────────────────────────────────────────────
4989
+ function start$3(iframe, cfg) {
4990
+ if (running$2) {
4991
+ logger$1.warn('Already running. Call stop() first.');
4992
+ return;
4993
+ }
4994
+ if (!iframe.contentDocument || !iframe.contentWindow) {
4995
+ throw new Error('Iframe document or window not accessible');
4996
+ }
4997
+ doc = iframe.contentDocument;
4998
+ win = iframe.contentWindow;
4999
+ config$1 = cfg;
5000
+ running$2 = true;
5001
+ shopFix = cfg.shopId != null ? get(cfg.shopId) : null;
5002
+ if (shopFix) {
5003
+ logger$1.log(`Shop fix loaded for "${cfg.shopId}":`, shopFix.description ?? '(no description)');
5004
+ }
5005
+ logger$1.configure({ enabled: !!cfg.debug });
5006
+ configure(!!cfg.debug);
5007
+ start$4(doc, win, cfg, { debug: !!cfg.debug });
5008
+ attach(cfg.debug);
5009
+ logger$1.log('Started');
5010
+ }
5011
+ function stop$3() {
5012
+ if (!running$2)
5013
+ return;
5014
+ stop$4();
5015
+ detach();
5016
+ doc = null;
5017
+ win = null;
5018
+ config$1 = null;
5019
+ shopFix = null;
5020
+ running$2 = false;
5021
+ logger$1.log('Stopped');
5022
+ }
5023
+ async function run() {
5024
+ if (!running$2 || !doc || !win || !config$1) {
5025
+ logger$1.warn('Not running. Call start() first.');
5026
+ return { height: 1000, width: 1000 };
5027
+ }
5028
+ const ctx = {
5029
+ doc,
5030
+ win,
5031
+ deviceType: config$1.deviceType,
5032
+ targetWidth: config$1.targetWidth,
5033
+ targetHeight: config$1.targetHeight,
5034
+ debug: config$1.debug,
5035
+ };
5036
+ const activeGlobal = getActiveFixes(ctx);
5037
+ if (activeGlobal.length > 0) {
5038
+ logger$1.log(`Active global fixes: ${activeGlobal.map((f) => f.name).join(', ')}`);
5039
+ }
5040
+ try {
5041
+ return await run$1(ctx, activeGlobal, shopFix);
5042
+ }
5043
+ catch (err) {
5044
+ logger$1.error('Critical error:', err);
5045
+ return { height: doc.body?.scrollHeight || 1000, width: doc.body?.scrollWidth || 1000 };
5046
+ }
6156
5047
  }
6157
5048
 
6158
5049
  const logger = createLogger({
@@ -6214,9 +5105,8 @@ async function initialize() {
6214
5105
  }
6215
5106
  }
6216
5107
  async function process() {
6217
- if (!iframe || !config) {
5108
+ if (!iframe || !config)
6218
5109
  return;
6219
- }
6220
5110
  if (!iframe.contentDocument || !iframe.contentWindow) {
6221
5111
  logger.error('Cannot access iframe document');
6222
5112
  config.onError?.(new Error('Cannot access iframe document'));
@@ -6224,11 +5114,8 @@ async function process() {
6224
5114
  }
6225
5115
  try {
6226
5116
  logger.log('Processing viewport units...');
6227
- // Start viewport replacer
6228
5117
  start$3(iframe, config);
6229
- // Setup navigation blocker
6230
5118
  start$5(iframe, { debug: config.debug });
6231
- // Run viewport replacement
6232
5119
  const result = await run();
6233
5120
  logger.log('Process completed:', result);
6234
5121
  config.onSuccess?.(result);
@@ -6265,91 +5152,30 @@ function enableNavigationBlocking$2() {
6265
5152
 
6266
5153
  /**
6267
5154
  * Iframe Helper Starter Module
6268
- * Manages iframe helper initialization with event listeners
6269
5155
  * @module start
6270
5156
  */
6271
5157
  // ============================================================================
6272
5158
  // State
6273
5159
  // ============================================================================
6274
5160
  let running = false;
6275
- let eventListenersAttached = false;
6276
- // Event listener references for cleanup
6277
- let dimensionsListener = null;
6278
- let navigationListener = null;
6279
- let formSubmitListener = null;
6280
- // ============================================================================
6281
- // Helper Functions
6282
- // ============================================================================
6283
- function attachEventListeners() {
6284
- if (eventListenersAttached) {
6285
- return;
6286
- }
6287
- dimensionsListener = ((e) => {
6288
- // console.log('[IframeHelper] Iframe dimensions finalized:', ev.detail);
6289
- });
6290
- navigationListener = ((e) => {
6291
- // console.warn('[IframeHelper] Iframe tried to navigate to:', ev.detail.url);
6292
- });
6293
- formSubmitListener = ((e) => {
6294
- // console.log('[IframeHelper] Iframe form submitted:', ev.detail.data);
6295
- });
6296
- window.addEventListener('iframe-dimensions-applied', dimensionsListener);
6297
- window.addEventListener('iframe-navigation-blocked', navigationListener);
6298
- window.addEventListener('iframe-form-submit', formSubmitListener);
6299
- eventListenersAttached = true;
6300
- }
6301
- function removeEventListeners() {
6302
- if (!eventListenersAttached) {
6303
- return;
6304
- }
6305
- if (dimensionsListener) {
6306
- window.removeEventListener('iframe-dimensions-applied', dimensionsListener);
6307
- dimensionsListener = null;
6308
- }
6309
- if (navigationListener) {
6310
- window.removeEventListener('iframe-navigation-blocked', navigationListener);
6311
- navigationListener = null;
6312
- }
6313
- if (formSubmitListener) {
6314
- window.removeEventListener('iframe-form-submit', formSubmitListener);
6315
- formSubmitListener = null;
6316
- }
6317
- eventListenersAttached = false;
6318
- }
6319
5161
  // ============================================================================
6320
5162
  // Public API
6321
5163
  // ============================================================================
6322
- /**
6323
- * Start iframe helper with configuration
6324
- * Initializes IframeFixer and attaches event listeners
6325
- */
6326
5164
  function start$1(config) {
6327
5165
  if (running) {
6328
5166
  console.warn('[IframeHelperStarter] Already running. Call stop() first.');
6329
5167
  return;
6330
5168
  }
6331
- // Attach global event listeners
6332
- attachEventListeners();
6333
- // Start iframe fixer
6334
5169
  start$2(config);
6335
5170
  running = true;
6336
5171
  }
6337
- /**
6338
- * Stop iframe helper and cleanup
6339
- */
6340
5172
  function stop$1() {
6341
5173
  if (!running) {
6342
5174
  return;
6343
5175
  }
6344
- // Stop iframe fixer
6345
5176
  stop$2();
6346
- // Remove event listeners
6347
- removeEventListeners();
6348
5177
  running = false;
6349
5178
  }
6350
- /**
6351
- * Enable navigation blocking
6352
- */
6353
5179
  function enableNavigationBlocking$1() {
6354
5180
  if (!running) {
6355
5181
  console.warn('[IframeHelperStarter] Not running. Call start() first.');
@@ -6373,11 +5199,12 @@ function useVizLiveRender() {
6373
5199
  const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
6374
5200
  const wrapperWidth = useHeatmapVizRectContext((s) => s.wrapperWidth);
6375
5201
  const setIsRenderViz = useHeatmapVizContext((s) => s.setIsRenderViz);
6376
- const contentWidth = useHeatmapWidthByDevice();
6377
- const htmlContent = useHeatmapLiveStore((state) => state.htmlContent);
6378
- const targetUrl = useHeatmapLiveStore((state) => state.targetUrl);
6379
- const renderMode = useHeatmapLiveStore((state) => state.renderMode);
6380
- const storefrontPassword = useHeatmapLiveStore((state) => state.storefrontPassword);
5202
+ const htmlContent = useHeatmapLiveStore((s) => s.htmlContent);
5203
+ const targetUrl = useHeatmapLiveStore((s) => s.targetUrl);
5204
+ const deviceType = useHeatmapSettingContext((s) => s.deviceType);
5205
+ const renderMode = useHeatmapLiveStore((s) => s.renderMode);
5206
+ const storefrontPassword = useHeatmapLiveStore((s) => s.storefrontPassword);
5207
+ useHeatmapWidthByDevice();
6381
5208
  const { iframeRef, isReady } = useVizLiveIframeMsg();
6382
5209
  // Handle iframe rendering based on mode
6383
5210
  useEffect(() => {
@@ -6418,14 +5245,14 @@ function useVizLiveRender() {
6418
5245
  if (!iframe || !hasContent)
6419
5246
  return;
6420
5247
  setIsRenderViz(true);
6421
- initIframeHelper$1(iframe, { width: wrapperWidth, height: wrapperHeight }, (height) => {
5248
+ initIframeHelper$1(iframe, deviceType, { width: wrapperWidth, height: wrapperHeight }, (height) => {
6422
5249
  height && setIframeHeight(height);
6423
5250
  setIsRenderViz(true);
6424
5251
  });
6425
5252
  return () => { };
6426
5253
  }, [
6427
5254
  isReady,
6428
- contentWidth,
5255
+ deviceType,
6429
5256
  wrapperHeight,
6430
5257
  wrapperWidth,
6431
5258
  renderMode,
@@ -6451,9 +5278,10 @@ function buildPortalUrl(targetUrl, storefrontPassword) {
6451
5278
  const portalServiceUrl = getPortalServiceUrl();
6452
5279
  return `${portalServiceUrl}/?${params.toString()}`;
6453
5280
  }
6454
- function initIframeHelper$1(iframe, rect, onSuccess) {
5281
+ function initIframeHelper$1(iframe, deviceType = EDeviceType.Desktop, rect, onSuccess) {
6455
5282
  stop();
6456
5283
  start({
5284
+ deviceType: deviceType,
6457
5285
  targetWidth: rect.width,
6458
5286
  targetHeight: rect.height,
6459
5287
  iframe: iframe,
@@ -6676,24 +5504,17 @@ class AttentionMapRenderer {
6676
5504
 
6677
5505
  class GXVisualizer extends Visualizer {
6678
5506
  attentionMap;
6679
- originalHtml;
6680
5507
  originalClearmap;
6681
5508
  originalSetup;
6682
5509
  constructor() {
6683
5510
  super();
6684
5511
  this.attentionMap = new AttentionMapRenderer(null);
6685
5512
  // Save references to base implementations before overriding
6686
- this.originalHtml = this.html;
6687
- this.originalClearmap = this.clearmap;
6688
5513
  this.originalSetup = this.setup;
6689
- this.html = this.htmlOverride;
5514
+ this.originalClearmap = this.clearmap;
6690
5515
  this.clearmap = this.clearmapOverride;
6691
5516
  this.setup = this.setupOverride;
6692
5517
  }
6693
- htmlOverride = async (decoded, target, portalCanvasId, hash, useproxy, logerror, shortCircuitStrategy) => {
6694
- await this.originalHtml(decoded, target, portalCanvasId, hash, useproxy, logerror, shortCircuitStrategy);
6695
- return this;
6696
- };
6697
5518
  setupOverride = async (target, options) => {
6698
5519
  // Clear existing custom renderers before re-initializing (null-safe)
6699
5520
  this.attentionMap?.clear();
@@ -6727,8 +5548,13 @@ const useHeatmapRender = () => {
6727
5548
  const setVizRef = useHeatmapVizRectContext((s) => s.setVizRef);
6728
5549
  const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
6729
5550
  const setIsRenderViz = useHeatmapVizContext((s) => s.setIsRenderViz);
5551
+ const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
5552
+ const contentWidth = useHeatmapWidthByDevice();
5553
+ const deviceType = useHeatmapSettingContext((s) => s.deviceType);
6730
5554
  const iframeRef = useRef(null);
6731
5555
  const renderHeatmap = useCallback(async (payloads) => {
5556
+ if (contentWidth === 0 || wrapperHeight === 0)
5557
+ return;
6732
5558
  if (!payloads || payloads.length === 0)
6733
5559
  return;
6734
5560
  const visualizer = vizRef ?? new GXVisualizer();
@@ -6739,12 +5565,13 @@ const useHeatmapRender = () => {
6739
5565
  if (!iframe?.contentWindow)
6740
5566
  return;
6741
5567
  await visualizer.html(payloads, iframe.contentWindow, viewId);
6742
- initIframeHelper(iframe, payloads, (height) => {
5568
+ const size = { width: contentWidth, height: wrapperHeight };
5569
+ initIframeHelper(iframe, deviceType, size, (height) => {
6743
5570
  height && setIframeHeight(height);
6744
5571
  setIsRenderViz(true);
6745
5572
  });
6746
5573
  // setIsRenderViz(true);
6747
- }, []);
5574
+ }, [wrapperHeight, contentWidth, deviceType]);
6748
5575
  useEffect(() => {
6749
5576
  if (!data || data.length === 0)
6750
5577
  return;
@@ -6753,23 +5580,23 @@ const useHeatmapRender = () => {
6753
5580
  return () => {
6754
5581
  setVizRef(null);
6755
5582
  };
6756
- }, [data]);
5583
+ }, [data, renderHeatmap, setVizRef]);
6757
5584
  return {
6758
5585
  iframeRef,
6759
5586
  };
6760
5587
  };
6761
- function initIframeHelper(iframe,
6762
- // size: { width: number; height: number },
6763
- payloads, onSuccess) {
6764
- const { size } = findLastSizeOfDom(payloads);
5588
+ function initIframeHelper(iframe, deviceType = EDeviceType.Desktop, size, onSuccess) {
6765
5589
  const docWidth = size.width ?? 0;
6766
5590
  const docHeight = size.height ?? 0;
5591
+ if (docHeight === 0)
5592
+ return;
6767
5593
  stop();
6768
5594
  start({
5595
+ deviceType: deviceType,
6769
5596
  targetWidth: docWidth,
6770
5597
  targetHeight: docHeight,
6771
5598
  iframe: iframe,
6772
- debug: false,
5599
+ debug: true,
6773
5600
  onSuccess: (data) => {
6774
5601
  iframe.style.height = `${data.height}px`;
6775
5602
  onSuccess(data.height);
@@ -6778,16 +5605,6 @@ payloads, onSuccess) {
6778
5605
  // fixer.recalculate();
6779
5606
  }
6780
5607
 
6781
- function isMobileDevice(userAgent) {
6782
- if (!userAgent)
6783
- return false;
6784
- return /android|webos|iphone|ipad|ipod|blackberry|windows phone|opera mini|iemobile|mobile|silk|fennec|bada|tizen|symbian|nokia|palmsource|meego|sailfish|kindle|playbook|bb10|rim/i.test(userAgent);
6785
- }
6786
-
6787
- function sortEvents(a, b) {
6788
- return a.time - b.time;
6789
- }
6790
-
6791
5608
  const useReplayRender = () => {
6792
5609
  const data = useHeatmapDataContext((s) => s.data);
6793
5610
  const setIsRenderViz = useHeatmapVizContext((s) => s.setIsRenderViz);
@@ -7189,30 +6006,6 @@ const useHeatmapScale = (props) => {
7189
6006
  handleScroll,
7190
6007
  };
7191
6008
  };
7192
- const useIframeHeight = (props) => {
7193
- const { iframeRef } = props;
7194
- const iframeWidth = useHeatmapWidthByDevice();
7195
- const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
7196
- const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
7197
- const isRenderViz = useHeatmapVizContext((s) => s.isRenderViz);
7198
- useIframeHeightProcessor({
7199
- iframeRef: iframeRef,
7200
- iframeWidth: iframeWidth,
7201
- defaultHeight: wrapperHeight,
7202
- deviceType: 1,
7203
- autoProcess: true,
7204
- watchHeightChanges: true,
7205
- autoApplyHeight: false,
7206
- shouldProcess: isRenderViz && !!wrapperHeight,
7207
- onHeightChange: (height) => {
7208
- setIframeHeight(height);
7209
- },
7210
- onProcessComplete: (result) => {
7211
- console.trace(`🚀 🐥 ~ VizDomRenderer ~ result:`, result);
7212
- setIframeHeight(result.height);
7213
- },
7214
- });
7215
- };
7216
6009
 
7217
6010
  const useWrapperRefHeight = (props) => {
7218
6011
  const setWrapperHeight = useHeatmapVizRectContext((s) => s.setWrapperHeight);
@@ -7412,10 +6205,10 @@ const useScrollmapZones = (options) => {
7412
6205
  const newZones = createZones(scrollmap);
7413
6206
  setZones(newZones);
7414
6207
  setIsReady(true);
7415
- logger$4.log(`[useScrollmap] Created ${newZones.length} zones in ${mode} mode`);
6208
+ logger$9.log(`[useScrollmap] Created ${newZones.length} zones in ${mode} mode`);
7416
6209
  }
7417
6210
  catch (error) {
7418
- logger$4.error('[useScrollmap] Error:', error);
6211
+ logger$9.error('[useScrollmap] Error:', error);
7419
6212
  setIsReady(false);
7420
6213
  }
7421
6214
  }, [enabled, scrollmap, mode, createZones]);
@@ -9055,16 +7848,16 @@ const ContentVizByMode = () => {
9055
7848
  ContentVizByMode.displayName = 'ContentVizByMode';
9056
7849
 
9057
7850
  const HeatmapPreview = () => {
9058
- return (jsxs(BoxStack, { id: "gx-hm-container", flexDirection: "row", overflow: "hidden", flex: "1", position: "relative", children: [jsx(ContentSidebar, {}), jsx(BoxStack, { flexDirection: "column", flex: "1", children: jsx(ContentRightPreview, {}) })] }));
7851
+ return (jsxs(BoxStack, { id: "gx-hm-container", flexDirection: "row", overflow: "hidden", flex: "1", position: "relative", children: [jsx(ContentSidebar, {}), jsxs(BoxStack, { flexDirection: "column", flex: "1", children: [jsx(ContentRightPreview, {}), jsx(ContentToolbar, {})] })] }));
9059
7852
  };
9060
7853
  const ContentRightPreview = () => {
9061
- const data = useHeatmapDataContext((state) => state.data);
9062
7854
  const CompEmptyState = useHeatmapControlStore((state) => state.controls.EmptyState);
9063
7855
  const isLoadingDom = useHeatmapSettingContext((state) => state.isLoadingDom);
9064
- if (!isLoadingDom && (!data || data.length === 0)) {
7856
+ const isEmptyData = useHeatmapDataContext((state) => state.isEmptyData);
7857
+ if (!isLoadingDom && isEmptyData) {
9065
7858
  return CompEmptyState ? jsx(CompEmptyState, {}) : 'EmptyState not found';
9066
7859
  }
9067
- return (jsxs(Fragment, { children: [jsx(ContentMetricBar, {}), jsx(BoxStack, { flexDirection: "column", flex: "1", children: jsx(ContentVizByMode, {}) }), jsx(ContentToolbar, {})] }));
7860
+ return (jsxs(Fragment, { children: [jsx(ContentMetricBar, {}), jsx(BoxStack, { flexDirection: "column", flex: "1", children: jsx(ContentVizByMode, {}) })] }));
9068
7861
  };
9069
7862
 
9070
7863
  const ContentTopBar = () => {
@@ -9108,4 +7901,4 @@ const HeatmapLayout = ({ data, clickmap, clickAreas, scrollmap, attentionMap, co
9108
7901
  }
9109
7902
  };
9110
7903
 
9111
- 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, useHeatmapLiveStore, useHeatmapRenderByMode, useHeatmapScale, useHeatmapScroll, useHeatmapScrollContext, useHeatmapSettingContext, useHeatmapVizContext, useHeatmapVizRectContext, useHeatmapWidthByDevice, useHoveredElement, useIframeHeight, useIframeHeightProcessor, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewIdContext, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };
7904
+ 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, useHeatmapLiveStore, useHeatmapRenderByMode, useHeatmapScale, useHeatmapScroll, useHeatmapScrollContext, useHeatmapSettingContext, useHeatmapVizContext, useHeatmapVizRectContext, useHeatmapWidthByDevice, useHoveredElement, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewIdContext, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };