acinguiux-preact-components 0.0.1

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 (313) hide show
  1. package/package.json +56 -0
  2. package/src/content/themes/theme-acinguiux-amg/theme-acinguiux-amg.css +23 -0
  3. package/src/content/themes/theme-acinguiux-cafe/theme-acinguiux-cafe.css +47 -0
  4. package/src/content/themes/theme-acinguiux-energy/theme-acinguiux-energy.css +45 -0
  5. package/src/content/themes/theme-acinguiux-livewire/theme-acinguiux-livewire.css +22 -0
  6. package/src/content/themes/theme-acinguiux-livewire-italy/theme-acinguiux-livewire-italy.css +22 -0
  7. package/src/content/themes/theme-acinguiux-recharge/theme-acinguiux-recharge.css +49 -0
  8. package/src/content/themes/theme-allon/theme-allon.css +25 -0
  9. package/src/content/themes/theme-atlas/theme-atlas.css +31 -0
  10. package/src/content/themes/theme-aurvana/resources/favicon/apple-touch-icon.png +0 -0
  11. package/src/content/themes/theme-aurvana/resources/favicon/favico.ico +0 -0
  12. package/src/content/themes/theme-aurvana/resources/favicon/favicon-96x96.png +0 -0
  13. package/src/content/themes/theme-aurvana/resources/favicon/favicon.ico +0 -0
  14. package/src/content/themes/theme-aurvana/resources/favicon/favicon.png +0 -0
  15. package/src/content/themes/theme-aurvana/resources/favicon/favicon.svg +13 -0
  16. package/src/content/themes/theme-aurvana/resources/favicon/google-touch-icon.png +0 -0
  17. package/src/content/themes/theme-aurvana/resources/favicon/manifest.json +14 -0
  18. package/src/content/themes/theme-aurvana/resources/favicon/site.webmanifest +21 -0
  19. package/src/content/themes/theme-aurvana/resources/favicon/web-app-manifest-192x192.png +0 -0
  20. package/src/content/themes/theme-aurvana/resources/favicon/web-app-manifest-512x512.png +0 -0
  21. package/src/content/themes/theme-aurvana/theme-aurvana.css +49 -0
  22. package/src/content/themes/theme-base/theme-base.css +49 -0
  23. package/src/content/themes/theme-base2/resources/favicon/android-chrome-192x192.png +0 -0
  24. package/src/content/themes/theme-base2/resources/favicon/android-chrome-512x512.png +0 -0
  25. package/src/content/themes/theme-base2/resources/favicon/apple-touch-icon.png +0 -0
  26. package/src/content/themes/theme-base2/resources/favicon/favico.ico +0 -0
  27. package/src/content/themes/theme-base2/resources/favicon/favicon-16x16.png +0 -0
  28. package/src/content/themes/theme-base2/resources/favicon/favicon-32x32.png +0 -0
  29. package/src/content/themes/theme-base2/resources/favicon/favicon-96x96.png +0 -0
  30. package/src/content/themes/theme-base2/resources/favicon/favicon.ico +0 -0
  31. package/src/content/themes/theme-base2/resources/favicon/favicon.png +0 -0
  32. package/src/content/themes/theme-base2/resources/favicon/favicon.svg +9 -0
  33. package/src/content/themes/theme-base2/resources/favicon/google-touch-icon.png +0 -0
  34. package/src/content/themes/theme-base2/resources/favicon/manifest.json +14 -0
  35. package/src/content/themes/theme-base2/resources/favicon/site.webmanifest +1 -0
  36. package/src/content/themes/theme-base2/resources/favicon/web-app-manifest-192x192.png +0 -0
  37. package/src/content/themes/theme-base2/resources/favicon/web-app-manifest-512x512.png +0 -0
  38. package/src/content/themes/theme-base2/resources/fonts/acinguiux-typeface-la-heavy-221208.woff2 +0 -0
  39. package/src/content/themes/theme-base2/theme-base2.css +47 -0
  40. package/src/content/themes/theme-eco-marathon/theme-eco-marathon.css +22 -0
  41. package/src/content/themes/theme-energy-transition-campus-amsterdam/theme-energy-transition-campus-amsterdam.css +26 -0
  42. package/src/content/themes/theme-evpass/theme-evpass.css +46 -0
  43. package/src/content/themes/theme-nam-2025/resources/favicon/apple-touch-icon.png +0 -0
  44. package/src/content/themes/theme-nam-2025/resources/favicon/favico.ico +0 -0
  45. package/src/content/themes/theme-nam-2025/resources/favicon/favicon-96x96.png +0 -0
  46. package/src/content/themes/theme-nam-2025/resources/favicon/favicon.ico +0 -0
  47. package/src/content/themes/theme-nam-2025/resources/favicon/favicon.png +0 -0
  48. package/src/content/themes/theme-nam-2025/resources/favicon/favicon.svg +9 -0
  49. package/src/content/themes/theme-nam-2025/resources/favicon/google-touch-icon.png +0 -0
  50. package/src/content/themes/theme-nam-2025/resources/favicon/manifest.json +14 -0
  51. package/src/content/themes/theme-nam-2025/resources/favicon/site.webmanifest +21 -0
  52. package/src/content/themes/theme-nam-2025/resources/favicon/web-app-manifest-192x192.png +0 -0
  53. package/src/content/themes/theme-nam-2025/resources/favicon/web-app-manifest-512x512.png +0 -0
  54. package/src/content/themes/theme-nam-2025/theme-nam-2025.css +47 -0
  55. package/src/content/themes/theme-pennzoil/theme-pennzoil.css +36 -0
  56. package/src/content/themes/theme-quaker-state/theme-quaker-state.css +63 -0
  57. package/src/content/themes/theme-tafawoq/theme-tafawoq.css +26 -0
  58. package/src/content/themes/theme-vegetable/resources/favicon/apple-touch-icon.png +0 -0
  59. package/src/content/themes/theme-vegetable/resources/favicon/favico.ico +0 -0
  60. package/src/content/themes/theme-vegetable/resources/favicon/favicon-96x96.png +0 -0
  61. package/src/content/themes/theme-vegetable/resources/favicon/favicon.ico +0 -0
  62. package/src/content/themes/theme-vegetable/resources/favicon/favicon.png +0 -0
  63. package/src/content/themes/theme-vegetable/resources/favicon/favicon.svg +13 -0
  64. package/src/content/themes/theme-vegetable/resources/favicon/google-touch-icon.png +0 -0
  65. package/src/content/themes/theme-vegetable/resources/favicon/manifest.json +14 -0
  66. package/src/content/themes/theme-vegetable/resources/favicon/site.webmanifest +21 -0
  67. package/src/content/themes/theme-vegetable/resources/favicon/web-app-manifest-192x192.png +0 -0
  68. package/src/content/themes/theme-vegetable/resources/favicon/web-app-manifest-512x512.png +0 -0
  69. package/src/content/themes/theme-vegetable/theme-vegetable.css +49 -0
  70. package/src/content/themes/theme-zeolyst/resources/fonts/type-ar-medium.woff2 +0 -0
  71. package/src/content/themes/theme-zeolyst/theme-zeolyst.css +29 -0
  72. package/src/main/atoms/audio.js +16 -0
  73. package/src/main/atoms/box.js +5 -0
  74. package/src/main/atoms/button.js +40 -0
  75. package/src/main/atoms/card.js +22 -0
  76. package/src/main/atoms/form.js +30 -0
  77. package/src/main/atoms/heading.js +17 -0
  78. package/src/main/atoms/icon.js +24 -0
  79. package/src/main/atoms/img.js +131 -0
  80. package/src/main/atoms/input.js +55 -0
  81. package/src/main/atoms/link-text.js +21 -0
  82. package/src/main/atoms/link.js +60 -0
  83. package/src/main/atoms/list.js +12 -0
  84. package/src/main/atoms/logo.js +9 -0
  85. package/src/main/atoms/menu.js +10 -0
  86. package/src/main/atoms/message.js +5 -0
  87. package/src/main/atoms/nav-link.js +49 -0
  88. package/src/main/atoms/popup.js +47 -0
  89. package/src/main/atoms/rich-text.js +128 -0
  90. package/src/main/atoms/scroller.js +224 -0
  91. package/src/main/atoms/svg.js +65 -0
  92. package/src/main/atoms/table.js +32 -0
  93. package/src/main/atoms/textarea.js +10 -0
  94. package/src/main/atoms/time.js +12 -0
  95. package/src/main/atoms/video.js +100 -0
  96. package/src/main/export-main.js +12 -0
  97. package/src/main/export-matter.js +86 -0
  98. package/src/main/export-preact-hooks.js +1 -0
  99. package/src/main/export-preact.js +1 -0
  100. package/src/main/index.js +13 -0
  101. package/src/main/molecules/asset.js +23 -0
  102. package/src/main/molecules/glossary.js +44 -0
  103. package/src/main/molecules/links.js +23 -0
  104. package/src/main/molecules/promo-text.js +27 -0
  105. package/src/main/molecules/tags.js +15 -0
  106. package/src/main/molecules/tree.js +51 -0
  107. package/src/main/organisms/accordion-item.js +106 -0
  108. package/src/main/organisms/author.js +29 -0
  109. package/src/main/organisms/breadcrumb.js +69 -0
  110. package/src/main/organisms/call-to-action.js +24 -0
  111. package/src/main/organisms/carousel.js +178 -0
  112. package/src/main/organisms/cart-item.js +156 -0
  113. package/src/main/organisms/cart.js +162 -0
  114. package/src/main/organisms/contact-form.js +141 -0
  115. package/src/main/organisms/container/ab-test.js +47 -0
  116. package/src/main/organisms/container/default.js +6 -0
  117. package/src/main/organisms/container/filtered-section.js +293 -0
  118. package/src/main/organisms/container/footer.js +12 -0
  119. package/src/main/organisms/container/grid.js +44 -0
  120. package/src/main/organisms/container/header.js +13 -0
  121. package/src/main/organisms/container/list.js +7 -0
  122. package/src/main/organisms/container/main.js +6 -0
  123. package/src/main/organisms/container/raw.js +7 -0
  124. package/src/main/organisms/container/section.js +28 -0
  125. package/src/main/organisms/container.js +29 -0
  126. package/src/main/organisms/content-owner.js +15 -0
  127. package/src/main/organisms/date-entry.js +56 -0
  128. package/src/main/organisms/external-search.js +73 -0
  129. package/src/main/organisms/filtered-item.js +163 -0
  130. package/src/main/organisms/footer-item.js +17 -0
  131. package/src/main/organisms/image-gallery.js +164 -0
  132. package/src/main/organisms/last-modified.js +20 -0
  133. package/src/main/organisms/legal-footer.js +16 -0
  134. package/src/main/organisms/list-item.js +48 -0
  135. package/src/main/organisms/metadata.js +11 -0
  136. package/src/main/organisms/navigation.js +232 -0
  137. package/src/main/organisms/notification.js +87 -0
  138. package/src/main/organisms/order-tracker.js +203 -0
  139. package/src/main/organisms/page-header-banner.js +26 -0
  140. package/src/main/organisms/page-header.js +33 -0
  141. package/src/main/organisms/page-tags.js +14 -0
  142. package/src/main/organisms/page.js +260 -0
  143. package/src/main/organisms/press-release.js +24 -0
  144. package/src/main/organisms/product-admin.js +204 -0
  145. package/src/main/organisms/promo-banner.js +28 -0
  146. package/src/main/organisms/promo-bottom.js +23 -0
  147. package/src/main/organisms/promo-button.js +8 -0
  148. package/src/main/organisms/promo-card-cover.js +35 -0
  149. package/src/main/organisms/promo-card.js +33 -0
  150. package/src/main/organisms/promo-full.js +20 -0
  151. package/src/main/organisms/promo-image.js +22 -0
  152. package/src/main/organisms/promo-lure.js +22 -0
  153. package/src/main/organisms/promo-product-card.js +187 -0
  154. package/src/main/organisms/promo-product-full.js +293 -0
  155. package/src/main/organisms/promo-simple.js +23 -0
  156. package/src/main/organisms/quote.js +21 -0
  157. package/src/main/organisms/search-form.js +42 -0
  158. package/src/main/organisms/search-nav.js +66 -0
  159. package/src/main/organisms/search-result.js +53 -0
  160. package/src/main/organisms/slider.js +26 -0
  161. package/src/main/organisms/standalone-asset.js +22 -0
  162. package/src/main/organisms/tabs.js +277 -0
  163. package/src/main/organisms/topbar.js +83 -0
  164. package/src/main/organisms/web-component.js +53 -0
  165. package/src/main/routing/annotation.js +9 -0
  166. package/src/main/routing/component.js +138 -0
  167. package/src/main/routing/empty.js +5 -0
  168. package/src/main/routing/error-handler.js +64 -0
  169. package/src/main/routing/placeholder-image.svg +5 -0
  170. package/src/main/routing/router.js +219 -0
  171. package/src/main/shared/analytics.js +677 -0
  172. package/src/main/shared/bubble-event.js +11 -0
  173. package/src/main/shared/custom-element.js +21 -0
  174. package/src/main/shared/deep-selector.js +28 -0
  175. package/src/main/shared/disable-transparency.js +10 -0
  176. package/src/main/shared/format-time.js +8 -0
  177. package/src/main/shared/get-id.js +5 -0
  178. package/src/main/shared/get-meta.js +3 -0
  179. package/src/main/shared/get-size-class.js +3 -0
  180. package/src/main/shared/get-size.js +11 -0
  181. package/src/main/shared/h.js +88 -0
  182. package/src/main/shared/hash-jump.js +33 -0
  183. package/src/main/shared/icons/arrow-back.svg +1 -0
  184. package/src/main/shared/icons/arrow-down.svg +1 -0
  185. package/src/main/shared/icons/arrow-next.svg +1 -0
  186. package/src/main/shared/icons/arrow-tail-right.svg +1 -0
  187. package/src/main/shared/icons/arrow-tail-up.svg +1 -0
  188. package/src/main/shared/icons/arrow-up.svg +1 -0
  189. package/src/main/shared/icons/asset-download.svg +1 -0
  190. package/src/main/shared/icons/logo.svg +5 -0
  191. package/src/main/shared/icons/low-carbon-placeholder.svg +9 -0
  192. package/src/main/shared/icons/media-pause.svg +1 -0
  193. package/src/main/shared/icons/media-play.svg +1 -0
  194. package/src/main/shared/icons/navigation-burger.svg +1 -0
  195. package/src/main/shared/icons/navigation-close.svg +1 -0
  196. package/src/main/shared/icons/navigation-link.svg +1 -0
  197. package/src/main/shared/icons/navigation-refresh.svg +1 -0
  198. package/src/main/shared/icons/navigation-search.svg +1 -0
  199. package/src/main/shared/icons/navigation-share.svg +1 -0
  200. package/src/main/shared/icons/toggle-newwindow.svg +1 -0
  201. package/src/main/shared/icons.js +18 -0
  202. package/src/main/shared/id-from-string.js +5 -0
  203. package/src/main/shared/mark-selection.js +19 -0
  204. package/src/main/shared/register.js +26 -0
  205. package/src/main/shared/renderer.js +43 -0
  206. package/src/main/shared/simple-consent-api.js +70 -0
  207. package/src/main/shared/split-links.js +11 -0
  208. package/src/main/shared/t.js +60 -0
  209. package/src/main/shared/twind.js +837 -0
  210. package/src/main/shared/update-head.js +34 -0
  211. package/src/main/shared/update-scrollbar-width.js +30 -0
  212. package/src/main/shared/use-link.js +151 -0
  213. package/src/main/shared/use-persistent-state.js +42 -0
  214. package/src/main/shared/wait-for-dom-ready.js +6 -0
  215. package/src/main/shared/wcm-mode.js +4 -0
  216. package/src/wcs/components/acinguiux-preact-doc/acinguiux-preact-doc.js +207 -0
  217. package/src/wcs/components/admin-dashboard/admin-dashboard.js +487 -0
  218. package/src/wcs/components/admin-login/admin-login.js +91 -0
  219. package/src/wcs/components/bazaar-voice/bazaar-voice.js +56 -0
  220. package/src/wcs/components/chatbot-koreai/chatbot-koreai.js +176 -0
  221. package/src/wcs/components/chatbot-koreai/koreai-transport.js +217 -0
  222. package/src/wcs/components/chatbot-ms/chatbot-ms.js +210 -0
  223. package/src/wcs/components/chatbot-test/chatbot-test.js +44 -0
  224. package/src/wcs/components/comparison-chart/comparison-chart.js +111 -0
  225. package/src/wcs/components/consent-banner/consent-banner.js +248 -0
  226. package/src/wcs/components/consent-banner/icons/ccpa.svg +6 -0
  227. package/src/wcs/components/consent-banner/icons/info.svg +1 -0
  228. package/src/wcs/components/consent-banner/provider-onetrust.js +131 -0
  229. package/src/wcs/components/decision-tree/arrow-back.svg +3 -0
  230. package/src/wcs/components/decision-tree/badges.js +37 -0
  231. package/src/wcs/components/decision-tree/decision-tree.js +162 -0
  232. package/src/wcs/components/dynamic-contact-details/dynamic-contact-details.js +111 -0
  233. package/src/wcs/components/example-accordion/example-accordion.js +10 -0
  234. package/src/wcs/components/example-asset/example-asset.js +12 -0
  235. package/src/wcs/components/example-form/example-form.js +59 -0
  236. package/src/wcs/components/example-nested/example-nested.js +10 -0
  237. package/src/wcs/components/example-routing/example-routing.js +51 -0
  238. package/src/wcs/components/example-rtl/example-rtl.js +28 -0
  239. package/src/wcs/components/example-tabs/example-tabs.js +12 -0
  240. package/src/wcs/components/example-web-component/example-web-component.js +34 -0
  241. package/src/wcs/components/floating-button/floating-button.js +17 -0
  242. package/src/wcs/components/formstack-form/fields/address.js +38 -0
  243. package/src/wcs/components/formstack-form/fields/checkbox.js +42 -0
  244. package/src/wcs/components/formstack-form/fields/date.js +22 -0
  245. package/src/wcs/components/formstack-form/fields/description.js +8 -0
  246. package/src/wcs/components/formstack-form/fields/input.js +8 -0
  247. package/src/wcs/components/formstack-form/fields/name.js +39 -0
  248. package/src/wcs/components/formstack-form/fields/radio.js +24 -0
  249. package/src/wcs/components/formstack-form/fields/rating.js +53 -0
  250. package/src/wcs/components/formstack-form/fields/section.js +8 -0
  251. package/src/wcs/components/formstack-form/fields/select.js +10 -0
  252. package/src/wcs/components/formstack-form/fields/textarea.js +8 -0
  253. package/src/wcs/components/formstack-form/fields/wrapper.js +11 -0
  254. package/src/wcs/components/formstack-form/formstack-form.js +280 -0
  255. package/src/wcs/components/fuel-prices/fuel-prices.js +45 -0
  256. package/src/wcs/components/furniture-overview/furniture-overview.js +115 -0
  257. package/src/wcs/components/gauge-value/gauge-value.js +65 -0
  258. package/src/wcs/components/help-centre/api.js +150 -0
  259. package/src/wcs/components/help-centre/help-centre.js +400 -0
  260. package/src/wcs/components/help-centre/icon-search.svg +1 -0
  261. package/src/wcs/components/image-gen/admin-panel.js +248 -0
  262. package/src/wcs/components/image-gen/image-gen.js +385 -0
  263. package/src/wcs/components/image-gen/labels.js +37 -0
  264. package/src/wcs/components/image-gen/use-api.js +392 -0
  265. package/src/wcs/components/inspired-gallery/inspired-gallery.js +118 -0
  266. package/src/wcs/components/launch-container/launch-container.js +95 -0
  267. package/src/wcs/components/launch-container/ledger.js +140 -0
  268. package/src/wcs/components/lng-map/lng-map.js +44 -0
  269. package/src/wcs/components/mouseflow-analytics/mouseflow-analytics.js +39 -0
  270. package/src/wcs/components/msds-search/msds-search.js +127 -0
  271. package/src/wcs/components/msds-search/navigation-search.svg +3 -0
  272. package/src/wcs/components/product-catalogue/icon-back.svg +3 -0
  273. package/src/wcs/components/product-catalogue/icon-cart.svg +3 -0
  274. package/src/wcs/components/product-catalogue/icon-close.svg +3 -0
  275. package/src/wcs/components/product-catalogue/product-catalogue.js +215 -0
  276. package/src/wcs/components/product-links/icon-cart.svg +3 -0
  277. package/src/wcs/components/product-links/product-links.js +43 -0
  278. package/src/wcs/components/rio-iframe/rio-iframe.js +137 -0
  279. package/src/wcs/components/salsify-products/filter-tools.js +60 -0
  280. package/src/wcs/components/salsify-products/icon-cart.svg +3 -0
  281. package/src/wcs/components/salsify-products/process-products.js +53 -0
  282. package/src/wcs/components/salsify-products/route-tools.js +54 -0
  283. package/src/wcs/components/salsify-products/salsify-products.js +281 -0
  284. package/src/wcs/components/shout-out/shout-out.js +51 -0
  285. package/src/wcs/components/simple-chart/simple-chart.js +53 -0
  286. package/src/wcs/components/single-stat/single-stat.js +85 -0
  287. package/src/wcs/components/site-feedback/site-feedback.js +56 -0
  288. package/src/wcs/components/skds-search/navigation-search.svg +3 -0
  289. package/src/wcs/components/skds-search/skds-search.js +103 -0
  290. package/src/wcs/components/smart-banner/smart-banner.js +104 -0
  291. package/src/wcs/components/standalone-table/arrow-up-down.svg +3 -0
  292. package/src/wcs/components/standalone-table/arrow-up.svg +3 -0
  293. package/src/wcs/components/standalone-table/standalone-table.js +440 -0
  294. package/src/wcs/components/station-locator/station-locator.js +49 -0
  295. package/src/wcs/components/store-badges/badges.js +60 -0
  296. package/src/wcs/components/store-badges/store-badges.js +93 -0
  297. package/src/wcs/components/topbar-button/person.svg +1 -0
  298. package/src/wcs/components/topbar-button/topbar-button.js +22 -0
  299. package/src/wcs/components/universal-gallery/universal-gallery.js +308 -0
  300. package/src/wcs/components/zendesk-chat/zendesk-chat.js +133 -0
  301. package/src/wcs/shared/chat-bot/README.md +61 -0
  302. package/src/wcs/shared/chat-bot/chat-bot.js +216 -0
  303. package/src/wcs/shared/chat-bot/resources/arrow-next.svg +1 -0
  304. package/src/wcs/shared/chat-bot/resources/navigation-close.svg +1 -0
  305. package/src/wcs/shared/chat-bot/resources/person.svg +1 -0
  306. package/src/wcs/shared/chat-bot/resources/upload.svg +1 -0
  307. package/src/wcs/shared/filtered-data/README.md +52 -0
  308. package/src/wcs/shared/filtered-data/fetch-data.js +33 -0
  309. package/src/wcs/shared/filtered-data/filtered-data.js +337 -0
  310. package/src/wcs/shared/promo-with-popup/icon-close.svg +3 -0
  311. package/src/wcs/shared/promo-with-popup/icon-next.svg +3 -0
  312. package/src/wcs/shared/promo-with-popup/icon-prev.svg +3 -0
  313. package/src/wcs/shared/promo-with-popup/promo-with-popup.js +93 -0
@@ -0,0 +1,677 @@
1
+ /* global location, customElements, localStorage, crypto */
2
+ import { getConsent } from '#acinguiux-preact/main/shared/simple-consent-api.js'
3
+ import { isPublish } from '#acinguiux-preact/main/shared/wcm-mode.js'
4
+ import deepSelector from '#acinguiux-preact/main/shared/deep-selector.js'
5
+
6
+ const DOWNLOADABLE_EXTENSIONS = ['zip', 'pdf', 'docx', 'xlsx', 'pptx', 'txt', 'csv', 'xps']
7
+ const SCROLL_DEBOUNCE = 500
8
+ const EXPERIMENT_SELECTOR = '[data-experiment]'
9
+ const USER_ID_STORAGE_KEY = 'aa_user_id'
10
+ const YT_ORIGINS = new Set(['https://www.youtube.com', 'https://www.youtube-nocookie.com'])
11
+ const MILESTONES = { event15: 90, event14: 75, event13: 50, event12: 25, event11: 1 }
12
+ const FORM_ERROR_TOLERANCE = 100
13
+ const DEFAULT_SAMPLE_RATE = 1
14
+ const SAMPLE_RATE = Number.parseFloat(localStorage.getItem('acinguiux-preact_sample_rate') ?? DEFAULT_SAMPLE_RATE)
15
+ const SKIP_TRACKING = SAMPLE_RATE < Math.random() // eslint-disable-line sonarjs/pseudo-random
16
+
17
+ // -----------------------------------------------------------------------------
18
+ // Private fields
19
+ // -----------------------------------------------------------------------------
20
+
21
+ let isInitialised = false
22
+ let userId
23
+ let newOrRepeat
24
+ let apiUrl
25
+ let pageViewParams = {}
26
+ let searchContext = []
27
+ let pageViewCount = 0
28
+ let scrolled = 0
29
+
30
+ // -----------------------------------------------------------------------------
31
+ // Adobe Analytics
32
+ // -----------------------------------------------------------------------------
33
+
34
+ // Using GET API as per the reference:
35
+ // https://github.com/AdobeDocs/analytics.en/blob/main/help/implement/validate/query-parameters.md
36
+ export async function send (params = {}) {
37
+ if (!apiUrl || !isPublish || SKIP_TRACKING) {
38
+ return null
39
+ }
40
+
41
+ const [resolvedUserId, resolvedNewOrRepeat] = getUserId()
42
+
43
+ // Set everything up for a page view.
44
+ if (!params.events) {
45
+ const pageUrl = getUrl(location.href)
46
+ const pageUrlWithoutParams = pageUrl.origin + pageUrl.pathname
47
+ const usp = pageUrl.searchParams
48
+ const campaign = pageUrl.search.includes('utm_')
49
+ ? ['medium', 'source', 'campaign', 'term', 'content'].map(p => usp.get(`utm_${p}`) || 'undefined').join(':')
50
+ : null
51
+
52
+ const isVanity = pageUrl.hash.startsWith('#vanity-')
53
+ const referrer = pageViewParams.v26 || document.referrer
54
+
55
+ searchContext = [] // Reset search context.
56
+ pageViewParams = {
57
+ // Standard properties.
58
+ bh: window.innerHeight,
59
+ bw: window.innerWidth,
60
+ c: window.screen.colorDepth,
61
+ ce: document.characterSet,
62
+ ch: document.title,
63
+ g: pageUrl.href,
64
+ r: referrer || 'No Referrer',
65
+ s: `${window.screen.availWidth}x${window.screen.availHeight}`,
66
+ server: pageUrl.host,
67
+ vid: resolvedUserId,
68
+
69
+ // Context evars.
70
+ v1: document.documentElement.lang.split('-').reverse().join('|'), // Locale.
71
+ v0: campaign,
72
+ v21: params.pageName,
73
+ v22: pageViewParams.pageName, // Get the previous page name.
74
+ v26: pageUrl.href, // Page URL.
75
+ v27: pageUrlWithoutParams,
76
+ v29: pageUrl.host,
77
+ v32: pageUrl.hash.startsWith('#vanity-') ? pageUrlWithoutParams : null, // Redirect URL.
78
+ v34: getTrafficOrigin(referrer),
79
+ v45: campaign,
80
+ v47: navigator.userAgent,
81
+ v49: resolvedNewOrRepeat,
82
+ v53: new Date().toISOString(),
83
+ v87: usp.get('gclid'),
84
+ v116: pageViewCount++,
85
+ v117: getColorScheme(),
86
+ v118: document.querySelector(EXPERIMENT_SELECTOR)?.dataset.experiment,
87
+
88
+ // Mapped properties.
89
+ c1: 'D=v1', // Locale
90
+ c9: 'D=v9', // Share medium
91
+ c11: 'D=v11', // Download file name
92
+ c12: 'D=v12', // Form name
93
+ c13: 'D=v13', // Last interacted form field
94
+ c14: 'D=v14', // Time take to complete the form
95
+ c16: 'D=v16', // Onsite search term
96
+ c17: 'D=v17', // Search type
97
+ c18: 'D=v18', // Search result count
98
+ c22: 'D=v22', // Previous page ID
99
+ c24: 'D=v24', // Subsection
100
+ c25: 'D=v25', // Vanity URL
101
+ c27: 'D=v27', // Page URL without params
102
+ c53: 'D=v53', // Timestamp
103
+ c69: 'D=v69' // Consent status
104
+ }
105
+
106
+ if (isVanity) {
107
+ pageViewParams.v25 = atob(pageUrl.hash.replace(/^#vanity-(.*)$/, '$1'))
108
+ pageViewParams.events = 'event17'
109
+ }
110
+ }
111
+
112
+ // Use previously saved page view params except for v69, which can change at any time.
113
+ params = { ...pageViewParams, v69: getConsentStatus(), ...params }
114
+
115
+ // Delete null or undefined parameters. Otherwise, URLSearchParams.toString() will serialize them.
116
+ for (const [k, v] of Object.entries(params)) {
117
+ if (v === null || v === undefined) {
118
+ delete params[k]
119
+ }
120
+ }
121
+
122
+ return fetch(`${apiUrl}?${new URLSearchParams(params).toString()}`)
123
+ }
124
+
125
+ // -----------------------------------------------------------------------------
126
+ // Helpers
127
+ // -----------------------------------------------------------------------------
128
+
129
+ // Local-storage session handling.
130
+ // https://github.com/AdobeDocs/analytics-1.4-apis/blob/master/docs/data-insertion-api/overview/c_visitor_id.md
131
+ export function getUserId () {
132
+ const hasConsent = getConsent()?.statistics
133
+ const storedUserId = hasConsent ? localStorage.getItem(USER_ID_STORAGE_KEY) : localStorage.removeItem(USER_ID_STORAGE_KEY)
134
+ const passedUserId = new URL(location).searchParams.get('vid')
135
+ if (!newOrRepeat) {
136
+ newOrRepeat = storedUserId ? 'repeat' : 'new'
137
+ }
138
+ userId = passedUserId ?? storedUserId ?? userId ?? crypto.randomUUID().replaceAll('-', '') // Adobe does not like hyphens.
139
+ hasConsent && localStorage.setItem(USER_ID_STORAGE_KEY, userId)
140
+
141
+ return [userId, newOrRepeat]
142
+ }
143
+
144
+ function getConsentStatus () {
145
+ const c = getConsent() ?? {}
146
+
147
+ if (c.statistics && c.preferences && c.marketing) {
148
+ return 'granted all'
149
+ } else if (c.statistics || c.preferences || c.marketing) {
150
+ return 'granted partial'
151
+ } else if (Object.keys(c).length === 0) {
152
+ return 'denied pending'
153
+ } else {
154
+ return 'denied all'
155
+ }
156
+ }
157
+
158
+ export function getUrl (href) {
159
+ const url = new URL(href, location)
160
+ url.searchParams.delete('vid') // Ignore vid parameter.
161
+ return url
162
+ }
163
+
164
+ function isExternalLink (urlObj) {
165
+ return urlObj.origin !== location.origin && urlObj.protocol?.startsWith('http')
166
+ }
167
+
168
+ function isPageLink (urlObj) {
169
+ const { hostname, pathname } = urlObj
170
+ return location.hostname === hostname && (pathname === '/' || pathname?.endsWith('.html'))
171
+ }
172
+
173
+ function isDownloadable (urlObj) {
174
+ return DOWNLOADABLE_EXTENSIONS.some(ext => urlObj.pathname?.endsWith(ext))
175
+ }
176
+
177
+ function getTrafficOrigin (referrer) {
178
+ if (!referrer) {
179
+ return 'No Referrer'
180
+ } else if (isExternalLink(getUrl(referrer))) {
181
+ return 'External'
182
+ }
183
+
184
+ return 'Internal'
185
+ }
186
+
187
+ function getColorScheme () {
188
+ return globalThis.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
189
+ }
190
+
191
+ function getFileName (link) {
192
+ return link.substring(link.lastIndexOf('/') + 1)
193
+ }
194
+
195
+ function extractLinkData (el) {
196
+ const urlObj = el.href ? getUrl(el.href) : {}
197
+
198
+ // Create copy in order to remove SVG and style elements. We only need text content.
199
+ const clone = el.cloneNode(true)
200
+ for (const elem of clone.querySelectorAll('svg, style')) {
201
+ elem.remove()
202
+ }
203
+
204
+ const data = {
205
+ pe: 'lnk_o',
206
+ text: clone.title || clone.ariaLabel || clone.textContent.trim(),
207
+ region: el.closest('header,main,footer')?.tagName || 'BODY',
208
+ action: 'click',
209
+ type: 'interaction',
210
+ href: urlObj.href
211
+ }
212
+
213
+ // The tracking event fires before the expand/collapse event.
214
+ // This means the expand/collapse attributes will be reversed.
215
+ if (el.ariaExpanded === 'true' || el.parentElement?.matches('details:not([open])')) {
216
+ data.action = 'expand'
217
+ } else if (el.ariaExpanded === 'false' || el.parentElement?.matches('details[open]')) {
218
+ data.action = 'collapse'
219
+ } else if (el.getAttribute('role') === 'tab') {
220
+ data.action = 'tab'
221
+ } else if (el.getAttribute('href')?.startsWith('#')) {
222
+ data.type = 'hash'
223
+ } else if (isExternalLink(urlObj)) {
224
+ data.type = 'external'
225
+ data.pe = 'lnk_e'
226
+ // Add vid to exit link.
227
+ if (userId) {
228
+ const url = new URL(el.href)
229
+ url.searchParams.set('vid', userId)
230
+ el.href = url.toString()
231
+ }
232
+ } else if (isPageLink(urlObj)) {
233
+ data.type = 'internal'
234
+ data.pe = 'lnk_o'
235
+ } else if (isDownloadable(urlObj)) {
236
+ data.type = 'download'
237
+ data.pe = 'lnk_d'
238
+ } else if (urlObj.protocol && !urlObj.protocol.startsWith('http')) {
239
+ data.type = urlObj.protocol.replace(':', '')
240
+ }
241
+
242
+ return data
243
+ }
244
+
245
+ function getComponentModel (el) {
246
+ return globalThis?.ami?.componentModels?.get?.(el?.closest('[data-name]')?.dataset?.key)
247
+ }
248
+
249
+ function getPageComponentModel () {
250
+ return getComponentModel(document.querySelector('[data-name="Page"]'))
251
+ }
252
+
253
+ function getPageProps () {
254
+ const model = getPageComponentModel()?.model ?? {}
255
+ const links = model.links ?? []
256
+ const metas = links.reduce((obj, link) => ({ ...obj, [link.name]: link.value }), {})
257
+ const trail = ['homepage', ...(metas?.hierarchy || '').split('/').slice(1)]
258
+ const nameTrail = trail.length === 1 ? trail : trail.slice(1) // Remove homepage from child pages.
259
+ const name = nameTrail.join('|')
260
+ const hierarchy = trail.slice(0, 3).join('|') // Limited to 3 levels
261
+ const [section, subsection] = nameTrail
262
+ return { name, hierarchy, section, subsection }
263
+ }
264
+
265
+ async function trackLinkClick (el, organism, experiment) {
266
+ const { pe, text, href, action, type, region } = extractLinkData(el)
267
+
268
+ const vars = {
269
+ events: 'event1',
270
+ pe,
271
+ pev1: href,
272
+ pev2: text,
273
+ v31: href,
274
+ v40: text,
275
+ v41: organism,
276
+ v42: action,
277
+ v43: type,
278
+ v118: experiment,
279
+ c40: 'D=v40', // Action text
280
+ c41: 'D=v41', // Containing organism
281
+ c42: 'D=v42', // Action
282
+ c43: 'D=v43' // Link type
283
+ }
284
+
285
+ // Download link.
286
+ if (pe === 'lnk_d') {
287
+ vars.events += ',event3'
288
+ vars.v11 = getFileName(href)
289
+ }
290
+
291
+ if (organism === 'SearchResult') {
292
+ const [searchTerm, resultCount] = getSearchContext()
293
+
294
+ vars.events += ',event6'
295
+ vars.v16 = `Onsite Search: ${searchTerm}`
296
+ vars.v17 = 'Onsite'
297
+ vars.v18 = `${resultCount}`
298
+ }
299
+
300
+ await send({ ...vars, ...getActivityMap(text, region) })
301
+ }
302
+
303
+ function getSearchContext () {
304
+ if (searchContext.length > 0) {
305
+ return searchContext
306
+ }
307
+
308
+ const pageComponentModel = getPageComponentModel()
309
+ if (!pageComponentModel) {
310
+ return []
311
+ }
312
+
313
+ // Track only if q parameter is present.
314
+ if (!new URLSearchParams(location.search).has('q')) {
315
+ return []
316
+ }
317
+
318
+ const componentModels = [...pageComponentModel.children]
319
+ for (const cm of componentModels) {
320
+ // Track only if SearchForm organism is found.
321
+ if (cm.organism === 'SearchForm') {
322
+ const link = cm.model?.links?.[1] ?? {}
323
+ searchContext = [link.name, Number(link.value)]
324
+ return searchContext
325
+ }
326
+
327
+ cm.children && componentModels.push(...cm.children)
328
+ }
329
+
330
+ return []
331
+ }
332
+
333
+ function getActivityMap (text, region) {
334
+ return {
335
+ 'c.': '',
336
+ 'a.': '',
337
+ 'activitymap.': '',
338
+ page: getPageProps()?.name,
339
+ link: text,
340
+ region,
341
+ pageIDType: 1,
342
+ '.activitymap': '',
343
+ '.a': '',
344
+ '.c': ''
345
+ }
346
+ }
347
+
348
+ // -----------------------------------------------------------------------------
349
+ // Tracking API
350
+ // -----------------------------------------------------------------------------
351
+
352
+ export function track (newApiUrl) {
353
+ apiUrl = newApiUrl
354
+
355
+ // Track events.
356
+ if (!isInitialised) {
357
+ isInitialised = true
358
+ trackLinks()
359
+ trackScroll()
360
+ trackYouTubeVideos()
361
+ trackMedia()
362
+ trackIframes().catch(console.error)
363
+ trackForms()
364
+ trackPageViews().catch(console.error)
365
+ }
366
+ }
367
+
368
+ async function trackPageViews () {
369
+ await trackPageView() // First time tracking.
370
+ globalThis.addEventListener('route', () => {
371
+ trackPageView()
372
+ })
373
+ }
374
+
375
+ async function trackPageView () {
376
+ scrolled = 0
377
+
378
+ const { name, section, subsection, hierarchy } = getPageProps()
379
+ await send({ pageName: name, v23: section, v24: subsection, h1: hierarchy })
380
+
381
+ // Track onsite search if applicable.
382
+ const [searchTerm, resultCount] = getSearchContext()
383
+ if (searchTerm) {
384
+ const events = `event4${resultCount === 0 ? ',event5' : ''}`
385
+ await send({
386
+ events,
387
+ pev1: getUrl(location).href,
388
+ pev2: 'Onsite Search',
389
+ pe: 'lnk_o',
390
+ v16: `Onsite Search: ${searchTerm}`,
391
+ v17: 'Onsite',
392
+ v18: `${resultCount}`
393
+ })
394
+ }
395
+ }
396
+
397
+ function trackLinks () {
398
+ document.addEventListener('click', event => {
399
+ // Get all the clickable elements on the path (works inside Shadow DOM).
400
+ // Ignore submit buttons.
401
+ const els = event.composedPath().filter(e => e?.matches?.('.clickable:not([type="submit"])'))
402
+
403
+ if (els.length === 0) {
404
+ return
405
+ }
406
+
407
+ const tagName = event.target.tagName.toLowerCase()
408
+ const customElementTagName = customElements.get(tagName) ? tagName : null
409
+
410
+ const handler = async () => {
411
+ for (const el of els) {
412
+ // Do not track button clicks inside a form.
413
+ if (!(el.closest('form') && el.tagName.toLowerCase() === 'button')) {
414
+ const experiment = el.closest(EXPERIMENT_SELECTOR)?.dataset.experiment
415
+ const organism = getComponentModel(el)?.organism ?? customElementTagName
416
+ await trackLinkClick(el, organism, experiment)
417
+ }
418
+ }
419
+ }
420
+ handler().catch(console.error)
421
+ })
422
+ }
423
+
424
+ function trackScroll () {
425
+ scrolled = 0
426
+
427
+ let resizeId
428
+ document.addEventListener('scroll', event => {
429
+ const target = event?.detail?.target ?? event.target
430
+ clearTimeout(resizeId)
431
+ resizeId = setTimeout(async () => {
432
+ if (target === document.body) {
433
+ await trackPageScroll()
434
+ } else if (target && target !== document) {
435
+ await trackSliderScroll(target)
436
+ }
437
+ }, SCROLL_DEBOUNCE)
438
+ }, true)
439
+ }
440
+
441
+ async function trackPageScroll () {
442
+ const el = document.body
443
+ if (el.scrollHeight <= el.clientHeight) {
444
+ return
445
+ }
446
+ const current = Math.round((el.scrollTop + el.clientHeight) / el.scrollHeight * 100)
447
+ const pageHref = getUrl(location).href
448
+ if (scrolled < 75 && current >= 75) {
449
+ await send({ events: 'event102', pe: 'lnk_o', pev1: pageHref, pev2: 'Scroll' })
450
+ scrolled = current
451
+ } else if (scrolled < 50 && current >= 50) {
452
+ await send({ events: 'event101', pe: 'lnk_o', pev1: pageHref, pev2: 'Scroll' })
453
+ scrolled = current
454
+ }
455
+ }
456
+
457
+ async function trackSliderScroll (target) {
458
+ const componentModel = target.closest('[data-name]')?.componentModel ?? {}
459
+ const index = Math.abs(Math.round(target.scrollLeft / target.children[0].offsetWidth)) + 1
460
+ if (index > 0) {
461
+ await send({
462
+ events: 'event97',
463
+ pe: 'lnk_o',
464
+ pev1: getUrl(location).href,
465
+ pev2: index,
466
+ v40: index,
467
+ v41: componentModel.organism,
468
+ v42: 'Slide/Swipe'
469
+ })
470
+ }
471
+ }
472
+
473
+ async function trackIframes () {
474
+ window.addEventListener('message', async event => {
475
+ const { action, actionType, assetName, location, text, url: link } = event.data
476
+
477
+ if (!action || !text || location !== 'iframe') {
478
+ return
479
+ }
480
+
481
+ // Remap iframe message to analytics parameters according to link tracking.
482
+ const parameterMap = {
483
+ ClickInternal: { v42: 'click', v43: 'internal' },
484
+ ClickExternal: { v42: 'click', v43: 'external', pe: 'lnk_e' },
485
+ ClickHash: { v42: 'click', v43: 'hash' },
486
+ ClickTab: { v42: 'tab', v43: 'interaction' },
487
+ Share: { v42: 'share', v43: 'interaction' },
488
+ Expand: { v42: 'expand', v43: 'interaction' },
489
+ Collapse: { v42: 'collapse', v43: 'interaction' },
490
+ 'Slide/SwipeNext': { v42: 'next', v43: 'interaction' },
491
+ 'Slide/SwipePrevious': { v42: 'prev', v43: 'interaction' },
492
+ Pagination: { v42: actionType, v43: 'interaction' },
493
+ Download: {
494
+ v42: 'click',
495
+ v43: 'download',
496
+ pe: 'lnk_d',
497
+ events: 'event1,event3',
498
+ pev1: text,
499
+ v31: text,
500
+ v11: getFileName(text)
501
+ }
502
+ }
503
+
504
+ const vars = {
505
+ events: 'event1',
506
+ pe: 'lnk_o',
507
+ pev1: link,
508
+ pev2: text,
509
+ v15: assetName,
510
+ v31: link,
511
+ v40: text,
512
+ v41: location,
513
+ c15: 'D=v15', // Asset name
514
+ c40: 'D=v40', // Text
515
+ c41: 'D=v41', // Location
516
+ c42: 'D=v42', // Action
517
+ c43: 'D=v43' // Action type
518
+ }
519
+
520
+ const key = Object.keys(parameterMap).find(k => k.startsWith(`${action}${actionType}`))
521
+ if (!key) {
522
+ console.warn(`Unknown iframe analytics action: ${action} ${actionType}`)
523
+ return
524
+ }
525
+
526
+ Object.assign(vars, parameterMap[key] || {})
527
+ await send(vars)
528
+ })
529
+ }
530
+
531
+ function sendMilestoneEvent (title, percentage, milestones = []) {
532
+ for (const milestone of milestones) {
533
+ if (percentage >= milestone.percent) {
534
+ if (!milestone.reached) {
535
+ milestone.reached = true
536
+ send({ events: milestone.event, v10: title }).catch(console.error)
537
+ }
538
+
539
+ break
540
+ }
541
+ }
542
+ }
543
+
544
+ function getPlaybackPercentage (currentTime, duration) {
545
+ return duration ? Math.round((currentTime / duration) * 100) : 0
546
+ }
547
+
548
+ function trackYouTubeVideos () {
549
+ // Bubbled-up load event.
550
+ document.addEventListener('load', event => {
551
+ const iframe = event.target
552
+ const url = getUrl(iframe.src)
553
+
554
+ if (YT_ORIGINS.has(url.origin) && url.searchParams.get('autoplay') !== '1') {
555
+ iframe.contentWindow?.postMessage(JSON.stringify({ event: 'listening', id: iframe.id }), url.origin)
556
+ iframe.milestones = Object.entries(MILESTONES).map(([ev, percent]) => ({ reached: false, percent, event: ev }))
557
+ }
558
+ })
559
+
560
+ // Common handler for all YouTube iframes
561
+ window.addEventListener('message', async event => {
562
+ // Only handle message coming from YouTube.
563
+ if (!YT_ORIGINS.has(event.origin)) {
564
+ return
565
+ }
566
+
567
+ // If YouTube changes the message format, we want to stop the processing here.
568
+ let data = event.data
569
+ if (typeof event.data === 'string') {
570
+ try {
571
+ data = JSON.parse(event.data)
572
+ } catch {
573
+ console.error('Could not parse YouTube postMessage data, analytics tracking aborted.')
574
+ return
575
+ }
576
+ }
577
+
578
+ // Find the corresponding iframe.
579
+ const [iframe] = await deepSelector(`#${data.id}`)
580
+ if (!iframe) {
581
+ return
582
+ }
583
+
584
+ if (['infoDelivery', 'initialDelivery'].includes(data.event)) {
585
+ const info = data.info
586
+ iframe.title ||= info.videoData?.title
587
+ const percentage = getPlaybackPercentage(info.currentTime, info.progressState?.duration)
588
+ sendMilestoneEvent(iframe.title, percentage, iframe.milestones)
589
+ }
590
+ })
591
+ }
592
+
593
+ function trackMedia () {
594
+ // Bubbled up timeupdate event.
595
+ document.addEventListener('timeupdate', event => {
596
+ const el = event.target
597
+ if (!el.autoplay) {
598
+ el.milestones ||= Object.entries(MILESTONES).map(([ev, percent]) => ({ reached: false, percent, event: ev }))
599
+ const percentage = getPlaybackPercentage(el.currentTime, el.duration)
600
+ sendMilestoneEvent(el.title, percentage, el.milestones)
601
+ }
602
+ })
603
+ }
604
+
605
+ function isValidForm (form) {
606
+ return form?.name
607
+ }
608
+
609
+ async function abandonmentHandler () {
610
+ const forms = await deepSelector('form')
611
+
612
+ for (const form of forms) {
613
+ if (form.lastInteractedField) {
614
+ await send({ events: 'event9', v12: form.name, v13: form.lastInteractedField })
615
+ }
616
+ }
617
+ }
618
+
619
+ function handleAbandonment () {
620
+ abandonmentHandler().catch(console.error)
621
+ }
622
+
623
+ function trackForms () {
624
+ document.addEventListener('input', event => {
625
+ const handler = async () => {
626
+ const input = event.composedPath()[0]
627
+ const form = input.closest('form')
628
+
629
+ if (isValidForm(form)) {
630
+ form.lastInteractedField = input.dataset.label
631
+ if (!form.startTime) {
632
+ form.startTime = Date.now()
633
+ await send({ events: 'event7', v12: form.name })
634
+ }
635
+ }
636
+ }
637
+ handler().catch(console.error)
638
+ })
639
+
640
+ document.addEventListener('invalid', event => {
641
+ const input = event.composedPath()[0]
642
+ const form = input.closest('form')
643
+
644
+ const handler = async () => {
645
+ await send({ events: 'event8', v12: form.name, l1: form.invalidLabels.join(',') })
646
+ delete form.invalidLabels
647
+ }
648
+
649
+ if (isValidForm(form)) {
650
+ form.invalidLabels ||= []
651
+ form.invalidLabels.push(input.dataset.label)
652
+ clearTimeout(form.timeout)
653
+ form.timeout = setTimeout(() => {
654
+ handler().catch(console.error)
655
+ }, FORM_ERROR_TOLERANCE)
656
+ }
657
+ })
658
+
659
+ document.addEventListener('submit', event => {
660
+ const submitHandler = async () => {
661
+ const form = event.composedPath()[0]
662
+
663
+ if (isValidForm(form)) {
664
+ const endTimeInSeconds = Math.ceil((Date.now() - form.startTime) / 1000)
665
+ delete form.startTime
666
+ delete form.lastInteractedField
667
+ delete form.invalidLabels
668
+ await send({ events: 'event10', v12: form.name, v14: endTimeInSeconds })
669
+ }
670
+ }
671
+
672
+ submitHandler().catch(console.error)
673
+ })
674
+
675
+ window.addEventListener('beforeunload', handleAbandonment)
676
+ document.addEventListener('route', handleAbandonment)
677
+ }
@@ -0,0 +1,11 @@
1
+ export default function bubbleEvent (event) {
2
+ if (!event.composed) {
3
+ event.stopPropagation()
4
+
5
+ event.target.dispatchEvent(new event.constructor(event.type, {
6
+ bubbles: true,
7
+ cancelable: true,
8
+ composed: true
9
+ }))
10
+ }
11
+ }
@@ -0,0 +1,21 @@
1
+ /* global HTMLElement, customElements */
2
+ import { injectStyles } from './twind.js'
3
+
4
+ /**
5
+ * @deprecated Use register() instead.
6
+ */
7
+ export default function customElement (elementName, callback) {
8
+ class CustomElement extends HTMLElement {
9
+ connectedCallback () {
10
+ const shadow = this.attachShadow({ mode: 'open' })
11
+ const container = document.createElement('span')
12
+ container.setAttribute('dir', document.documentElement.dir) // Apply text direction.
13
+ shadow.appendChild(container)
14
+ injectStyles(shadow)
15
+ const attrs = Object.assign({}, ...Array.from(this.attributes, ({ name, value }) => ({ [name]: value })))
16
+ callback(attrs, container, shadow)
17
+ }
18
+ }
19
+
20
+ customElements.get(elementName) || customElements.define(elementName, CustomElement)
21
+ }