/** * The published site's popup runtime: an inline script in `_document`'s * ``, right after `window.__tqPopups = ` (see * project-plugin.ts). Dependency-free ES5, so it runs before any bundle. * * Each popup is a component instance at the end of every page (the editor's * mapper puts it there). Its open workflow listens for the window event * `workflow:custom:tq-popup-open-`, and its card — drawn only while it is * open — carries `data-tq-popup-root=""`. This script decides whether and * when to send that event; everything the visitor sees is the component. * * Per page view (on load and on every client-side route change, i.e. a new * pathname through `history.pushState`/`replaceState` or `popstate`): * - nothing on an excluded path, nor on a page showing a checkout or * purchase view (`data-tq-commerce-view`, checked again when it fires); * - the first eligible popup: an `only` rule naming the path beats an * `except` rule beats `all`, then the newer (later in the list); eligible = * not shown within its frequency (`tq_popup:` in localStorage, or * sessionStorage for once per session) and the right audience (returning = * `tq_popup_visited` was already set when this document loaded; it is set * at the end of every evaluation); * - its trigger armed: exit intent on computers (>= 1024px), the delay, the * scroll depth, or the next frame — the phone trigger below 1024px; * - one popup per page view: once it has been shown nothing else opens until * the next route change. * * Opening: the event is sent again every half second (for up to ten seconds) * until the card is on the page — the component's listener is attached only * once the page has hydrated, which a head script cannot wait for. Only then * is the popup counted shown: the frequency is written and `popup_shown` * tracked. While the cookie banner is on the page nothing is sent: a popup * would stand between the visitor and that answer, so it waits, and opens the * moment the banner goes. * * While a card is open: a click on a `[data-tq-popup-convert]` control or a * form submit inside it tracks `popup_converted` (once per opening); a * converting link also closes the popup right after the click (closing it * during the click would take the link out of the page before it can * navigate); `[data-tq-popup-code]` copies its code and says "Copied" for a * moment; Escape closes a corner or bottom popup (a centred one is a modal * dialog with its own Escape). Closing = clicking the card's ✕, so the * component's own close workflow runs. * * Analytics go through `window.tpAnalytics.track(name, { recordId })` when the * tracker is on the page; nothing here throws without it. * * ⛔ Shipped as the body of a template literal: no backticks, no dollar-brace, * and no closing-tag sequence anywhere in it. */ export declare const POPUP_RUNTIME_SOURCE = "(function () {\n var config = window.__tqPopups;\n if (!config || !config.popups || !config.popups.length) {\n return;\n }\n var POPUPS = config.popups;\n var EXCLUDED_PATHS = config.excludedPaths || [];\n var OPEN_EVENT_PREFIX = 'workflow:custom:tq-popup-open-';\n var ROOT_ATTR = 'data-tq-popup-root';\n var CODE_ATTR = 'data-tq-popup-code';\n var CONVERT_SELECTOR = '[data-tq-popup-convert]';\n var CLOSE_SELECTOR = '[data-thq=\"thq-modal-close-button\"]';\n var CHECKOUT_SELECTOR = '[data-tq-commerce-view=\"begin_checkout\"],[data-tq-commerce-view=\"purchase\"]';\n var COOKIE_BANNER_SELECTOR = '[data-thq=\"thq-cookie-consent\"]';\n var FREQUENCY_PREFIX = 'tq_popup:';\n var VISITED_KEY = 'tq_popup_visited';\n var DESKTOP_QUERY = '(min-width: 1024px)';\n var DESKTOP_MIN_WIDTH = 1024;\n var DAY_MS = 86400000;\n var OPEN_RETRY_MS = 500;\n var OPEN_ATTEMPTS = 20;\n var COPIED_LABEL = 'Copied';\n var COPIED_MS = 1500;\n var SHOW_TEXT = 4;\n\n var run = 0;\n var cleanups = [];\n var currentPath = null;\n var openings = {};\n\n function read(area, key) {\n try {\n var store = window[area];\n return store ? store.getItem(key) : null;\n } catch (e) {\n return null;\n }\n }\n\n function write(area, key, value) {\n try {\n var store = window[area];\n if (store) {\n store.setItem(key, value);\n }\n } catch (e) {}\n }\n\n var returning = read('localStorage', VISITED_KEY) !== null;\n\n function track(name, id) {\n try {\n var analytics = window.tpAnalytics;\n if (analytics && typeof analytics.track === 'function') {\n analytics.track(name, { recordId: id });\n }\n } catch (e) {}\n }\n\n function later(fn, ms) {\n var token = run;\n var timer = window.setTimeout(function () {\n if (token === run) {\n fn();\n }\n }, ms);\n cleanups.push(function () {\n window.clearTimeout(timer);\n });\n }\n\n function nextFrame(fn) {\n if (typeof window.requestAnimationFrame !== 'function') {\n later(fn, 0);\n return;\n }\n var token = run;\n var frame = window.requestAnimationFrame(function () {\n if (token === run) {\n fn();\n }\n });\n cleanups.push(function () {\n window.cancelAnimationFrame(frame);\n });\n }\n\n function listen(target, type, handler, options) {\n target.addEventListener(type, handler, options);\n cleanups.push(function () {\n target.removeEventListener(type, handler, options);\n });\n }\n\n function cancel() {\n run++;\n var pending = cleanups;\n cleanups = [];\n for (var i = 0; i < pending.length; i++) {\n try {\n pending[i]();\n } catch (e) {}\n }\n }\n\n function normalizePath(path) {\n var clean = String(path || '/').split('?')[0].split('#')[0] || '/';\n while (clean.length > 1 && clean.charAt(clean.length - 1) === '/') {\n clean = clean.slice(0, -1);\n }\n return clean;\n }\n\n function pathMatches(path, pattern) {\n if (typeof pattern !== 'string' || !pattern) {\n return false;\n }\n if (pattern.charAt(pattern.length - 1) === '*') {\n return path.indexOf(pattern.slice(0, -1)) === 0;\n }\n return path === normalizePath(pattern);\n }\n\n function matchesAny(path, patterns) {\n for (var i = 0; i < patterns.length; i++) {\n if (pathMatches(path, patterns[i])) {\n return true;\n }\n }\n return false;\n }\n\n function pageRank(popup, path) {\n var rule = popup.pages || {};\n var paths = rule.paths || [];\n if (rule.mode === 'only') {\n return matchesAny(path, paths) ? 3 : 0;\n }\n if (rule.mode === 'except') {\n return matchesAny(path, paths) ? 0 : 2;\n }\n return 1;\n }\n\n function storageAreaFor(popup) {\n return popup.frequency && popup.frequency.kind === 'session' ? 'sessionStorage' : 'localStorage';\n }\n\n function shownRecently(popup) {\n var frequency = popup.frequency || {};\n var last = read(storageAreaFor(popup), FREQUENCY_PREFIX + popup.id);\n if (last === null) {\n return false;\n }\n if (frequency.kind === 'once' || frequency.kind === 'session') {\n return true;\n }\n var shownAt = Number(last);\n if (!shownAt) {\n return false;\n }\n return Date.now() - shownAt < (Number(frequency.days) || 0) * DAY_MS;\n }\n\n function audienceAllows(popup) {\n if (popup.audience === 'new') {\n return !returning;\n }\n if (popup.audience === 'returning') {\n return returning;\n }\n return true;\n }\n\n function pickPopup(path) {\n var ranked = [];\n for (var i = 0; i < POPUPS.length; i++) {\n var rank = pageRank(POPUPS[i], path);\n if (rank > 0) {\n ranked.push({ popup: POPUPS[i], rank: rank, index: i });\n }\n }\n ranked.sort(function (a, b) {\n return b.rank - a.rank || b.index - a.index;\n });\n for (var j = 0; j < ranked.length; j++) {\n if (!shownRecently(ranked[j].popup) && audienceAllows(ranked[j].popup)) {\n return ranked[j].popup;\n }\n }\n return null;\n }\n\n function popupById(id) {\n for (var i = 0; i < POPUPS.length; i++) {\n if (POPUPS[i].id === id) {\n return POPUPS[i];\n }\n }\n return null;\n }\n\n function checkoutOnScreen() {\n return !!document.querySelector(CHECKOUT_SELECTOR);\n }\n\n function cookieBannerOnScreen() {\n return !!document.querySelector(COOKIE_BANNER_SELECTOR);\n }\n\n function isComputer() {\n try {\n if (typeof window.matchMedia === 'function') {\n return window.matchMedia(DESKTOP_QUERY).matches;\n }\n } catch (e) {}\n return window.innerWidth >= DESKTOP_MIN_WIDTH;\n }\n\n function scrolledPercent() {\n var root = document.documentElement;\n var height = Math.max(root.scrollHeight || 0, document.body ? document.body.scrollHeight || 0 : 0);\n var room = height - window.innerHeight;\n if (room <= 0) {\n return 100;\n }\n var top = window.pageYOffset || root.scrollTop || 0;\n return (top / room) * 100;\n }\n\n function cardOf(id) {\n var cards = document.querySelectorAll('[' + ROOT_ATTR + ']');\n for (var i = 0; i < cards.length; i++) {\n if (cards[i].getAttribute(ROOT_ATTR) === id) {\n return cards[i];\n }\n }\n return null;\n }\n\n function onPage(element) {\n return !!element && document.documentElement.contains(element);\n }\n\n function closeCard(card) {\n var button = card.querySelector(CLOSE_SELECTOR);\n if (onPage(button)) {\n button.click();\n }\n }\n\n function markShown(popup) {\n write(storageAreaFor(popup), FREQUENCY_PREFIX + popup.id, String(Date.now()));\n openings[popup.id] = { converted: false };\n track('popup_shown', popup.id);\n }\n\n function openPopup(popup) {\n var attempts = 0;\n var settled = false;\n var waitingForBanner = false;\n var observer = null;\n function settle() {\n settled = true;\n if (observer) {\n observer.disconnect();\n observer = null;\n }\n }\n function checkCard() {\n if (settled) {\n return;\n }\n if (cardOf(popup.id)) {\n settle();\n markShown(popup);\n } else if (waitingForBanner && !cookieBannerOnScreen()) {\n waitingForBanner = false;\n send();\n }\n }\n function send() {\n if (settled) {\n return;\n }\n if (checkoutOnScreen()) {\n settle();\n return;\n }\n if (observer && cookieBannerOnScreen()) {\n waitingForBanner = true;\n return;\n }\n attempts++;\n window.dispatchEvent(new window.CustomEvent(OPEN_EVENT_PREFIX + popup.id, { detail: {} }));\n checkCard();\n if (settled) {\n return;\n }\n if (attempts < OPEN_ATTEMPTS) {\n later(send, OPEN_RETRY_MS);\n } else {\n settle();\n }\n }\n if (typeof window.MutationObserver === 'function') {\n observer = new window.MutationObserver(checkCard);\n observer.observe(document.documentElement, { childList: true, subtree: true });\n cleanups.push(settle);\n }\n send();\n }\n\n function arm(popup) {\n var fired = false;\n function fire() {\n if (fired) {\n return;\n }\n fired = true;\n if (!checkoutOnScreen()) {\n openPopup(popup);\n }\n }\n var trigger = popup.trigger || {};\n var kind = isComputer() ? trigger.desktop : trigger.phone;\n if (kind === 'exit-intent') {\n listen(document, 'mouseout', function (event) {\n if (!event.relatedTarget && event.clientY <= 0) {\n fire();\n }\n });\n } else if (kind === 'scroll') {\n var depth = Number(trigger.scrollPercent) || 0;\n listen(\n window,\n 'scroll',\n function () {\n if (scrolledPercent() >= depth) {\n fire();\n }\n },\n { passive: true }\n );\n } else if (kind === 'delay') {\n later(fire, Math.max(0, Number(trigger.delaySeconds) || 0) * 1000);\n } else {\n nextFrame(fire);\n }\n }\n\n function evaluate() {\n cancel();\n currentPath = normalizePath(window.location.pathname);\n try {\n if (!matchesAny(currentPath, EXCLUDED_PATHS) && !checkoutOnScreen()) {\n var popup = pickPopup(currentPath);\n if (popup) {\n arm(popup);\n }\n }\n } catch (e) {}\n write('localStorage', VISITED_KEY, '1');\n }\n\n function onRouteChange() {\n try {\n var path = normalizePath(window.location.pathname);\n if (path === currentPath) {\n return;\n }\n cancel();\n currentPath = path;\n later(evaluate, 0);\n } catch (e) {}\n }\n\n function patchHistory(method) {\n var history = window.history;\n var original = history && history[method];\n if (typeof original !== 'function') {\n return;\n }\n history[method] = function () {\n var result = original.apply(this, arguments);\n onRouteChange();\n return result;\n };\n }\n\n function elementOf(target) {\n if (!target) {\n return null;\n }\n return target.nodeType === 1 ? target : target.parentElement || null;\n }\n\n function cardAround(element) {\n return element && typeof element.closest === 'function' ? element.closest('[' + ROOT_ATTR + ']') : null;\n }\n\n function converted(id) {\n var opening = openings[id];\n if (opening && opening.converted) {\n return;\n }\n openings[id] = { converted: true };\n track('popup_converted', id);\n }\n\n function labelNode(element) {\n var walker = document.createTreeWalker(element, SHOW_TEXT);\n var node = walker.nextNode();\n while (node && !String(node.nodeValue).trim()) {\n node = walker.nextNode();\n }\n return node;\n }\n\n function sayCopied(button) {\n var node = labelNode(button);\n if (!node) {\n return;\n }\n if (button.__tqCopiedTimer) {\n window.clearTimeout(button.__tqCopiedTimer);\n } else {\n button.__tqCopiedLabel = node.nodeValue;\n }\n node.nodeValue = COPIED_LABEL;\n button.__tqCopiedTimer = window.setTimeout(function () {\n button.__tqCopiedTimer = null;\n if (node.nodeValue === COPIED_LABEL) {\n node.nodeValue = button.__tqCopiedLabel;\n }\n }, COPIED_MS);\n }\n\n function copyWithTextarea(text, host) {\n var parent = onPage(host) ? host : document.body;\n var focused = document.activeElement;\n var area = document.createElement('textarea');\n area.value = text;\n area.setAttribute('readonly', '');\n area.setAttribute('aria-hidden', 'true');\n area.style.position = 'fixed';\n area.style.top = '0';\n area.style.left = '0';\n area.style.opacity = '0';\n parent.appendChild(area);\n var copied = false;\n try {\n area.focus();\n area.select();\n copied = document.execCommand('copy') === true;\n } catch (e) {}\n parent.removeChild(area);\n if (focused && typeof focused.focus === 'function') {\n focused.focus();\n }\n return copied;\n }\n\n function copyCode(button, card) {\n var code = button.getAttribute(CODE_ATTR);\n if (!code) {\n return;\n }\n function fallback() {\n if (copyWithTextarea(code, card)) {\n sayCopied(button);\n }\n }\n try {\n var clipboard = window.navigator.clipboard;\n if (clipboard && typeof clipboard.writeText === 'function') {\n clipboard.writeText(code).then(function () {\n sayCopied(button);\n }, fallback);\n return;\n }\n } catch (e) {}\n fallback();\n }\n\n function onClick(event) {\n try {\n var target = elementOf(event.target);\n var card = cardAround(target);\n if (!card) {\n return;\n }\n var codeButton = target.closest('[' + CODE_ATTR + ']');\n if (codeButton && card.contains(codeButton)) {\n copyCode(codeButton, card);\n }\n var control = target.closest(CONVERT_SELECTOR);\n if (control && card.contains(control)) {\n converted(card.getAttribute(ROOT_ATTR));\n var link = target.closest('a[href]');\n if (link && card.contains(link)) {\n window.setTimeout(function () {\n closeCard(card);\n }, 0);\n }\n }\n } catch (e) {}\n }\n\n function onSubmit(event) {\n try {\n var card = cardAround(elementOf(event.target));\n if (card) {\n converted(card.getAttribute(ROOT_ATTR));\n }\n } catch (e) {}\n }\n\n function onKeyDown(event) {\n if (event.key !== 'Escape' && event.key !== 'Esc') {\n return;\n }\n try {\n var cards = document.querySelectorAll('[' + ROOT_ATTR + ']');\n for (var i = 0; i < cards.length; i++) {\n var popup = popupById(cards[i].getAttribute(ROOT_ATTR));\n if (popup && (popup.layout === 'corner' || popup.layout === 'bottom')) {\n closeCard(cards[i]);\n }\n }\n } catch (e) {}\n }\n\n try {\n document.addEventListener('click', onClick, true);\n document.addEventListener('submit', onSubmit, true);\n document.addEventListener('keydown', onKeyDown);\n patchHistory('pushState');\n patchHistory('replaceState');\n window.addEventListener('popstate', onRouteChange);\n currentPath = normalizePath(window.location.pathname);\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', evaluate);\n } else {\n evaluate();\n }\n } catch (e) {}\n})();"; //# sourceMappingURL=popup-runtime-source.d.ts.map