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