(() => { const getConfig = function () { const fourteenLoadedEventName = "FOURTEEN_LOADED"; const fourteenOrigin = "https://widget.14.ai"; const iframe = document.createElement("iframe"); iframe.allow = "clipboard-write"; let didLoad = false; let isEmbedReady = false; let onReadyCallbacks = []; let onOpenChangeCallback = null; let onNotificationsChangeCallback = null; let bubbleState = { isExpanded: false, bubbleWidth: 64, bubbleHeight: 64 }; const sendMessage = (message) => { if (didLoad) { iframe.contentWindow.postMessage(message, fourteenOrigin); } else { window.addEventListener( fourteenLoadedEventName, () => { iframe.contentWindow.postMessage(message, fourteenOrigin); }, { once: true } ); } }; let resizeTimeout = null; const calculateAndSetIframeDimensions = (immediate = false) => { // Clear any pending resize if (resizeTimeout) { clearTimeout(resizeTimeout); resizeTimeout = null; } const performResize = () => { if (bubbleState.isExpanded) { // Panel dimensions: ensure panel + internal padding fits within viewport const maxWidthFromScreen = window.innerWidth - 60; // 40px padding + 20px margin const panelWidth = Math.min(maxWidthFromScreen, 440); // Check if full panel height (700px + 60px offset) exceeds window height const idealPanelHeight = 700; const requiredHeightWithOffset = idealPanelHeight + 60; let panelHeight, iframeHeight; if (requiredHeightWithOffset > window.innerHeight) { // Panel is too tall - use full window height, no top offset panelHeight = window.innerHeight - 40; // Only account for internal padding iframeHeight = window.innerHeight; } else { // Panel fits with offset - use normal calculation const maxHeightFromScreen = window.innerHeight - 60; // 40px padding + 20px margin panelHeight = Math.min(maxHeightFromScreen, idealPanelHeight); iframeHeight = panelHeight + 40; } // Set iframe size iframe.style.width = (panelWidth + 40) + "px"; iframe.style.height = iframeHeight + "px"; // Always position iframe at bottom-right with 0 offset iframe.style.bottom = "0px"; iframe.style.right = "0px"; } else { // Bubble dimensions iframe.style.width = (bubbleState.bubbleWidth + 40) + "px"; iframe.style.height = (bubbleState.bubbleHeight + 40) + "px"; // Always position iframe at bottom-right with 0 offset iframe.style.bottom = "0px"; iframe.style.right = "0px"; } }; if (immediate) { performResize(); } else { // Delay resize to match bubble animation duration // Spring with stiffness 350, damping 28 takes roughly 800-1000ms to settle resizeTimeout = setTimeout(() => { performResize(); }, 300); } }; const setPopupOpen = (open) => { console.debug('setPopupOpen', open); }; return { init: function () { const load = () => { iframe.src = "https://widget.14.ai/sphShxZI0MdxMeBz"; iframe.id = "sphShxZI0MdxMeBz"; iframe.style.border = 0; iframe.style.padding = 0; iframe.style.margin = 0; iframe.style.position = "fixed"; iframe.style.zIndex = 9999; iframe.style.right = 0; iframe.style.bottom = 0; iframe.style.outline = "none"; iframe.style.display = "none"; iframe.style.background = "transparent"; iframe.allowTransparency = "true"; iframe.style.boxSizing = "borderBox"; iframe.style.userSelect = "none"; iframe.style.opacity = "0"; iframe.style.transition = "opacity 200ms ease-in-out"; iframe.addEventListener("load", () => { iframe.style.display = "block"; didLoad = true; calculateAndSetIframeDimensions(true); // Immediate on load window.dispatchEvent(new CustomEvent(fourteenLoadedEventName)); setTimeout(() => { iframe.style.opacity = "1"; }, 500); }); // Listen for window resize to update iframe dimensions immediately window.addEventListener("resize", () => { calculateAndSetIframeDimensions(true); }); const container = document.getElementById("fourteen-container-sphShxZI0MdxMeBz") ?? document.body; container.appendChild(iframe); window.addEventListener("message", (event) => { const eventType = event.data?.type; switch (eventType) { case "FOURTEEN_EMBED_OPEN_POPUP_sphShxZI0MdxMeBz": { onOpenChangeCallback?.(true); break; } case "FOURTEEN_EMBED_CLOSE_POPUP_sphShxZI0MdxMeBz": { onOpenChangeCallback?.(false); break; } case "FOURTEEN_EMBED_NOTIFICATIONS_CHANGE_sphShxZI0MdxMeBz": { onNotificationsChangeCallback?.(event.data.numNotifications); break; } case "FOURTEEN_EMBED_READY_sphShxZI0MdxMeBz": { isEmbedReady = true; onReadyCallbacks.forEach((callback) => callback()); onReadyCallbacks = []; break; } case "FOURTEEN_EMBED_UNREADY_sphShxZI0MdxMeBz": { isEmbedReady = false; break; } case "FOURTEEN_EMBED_RESIZE_sphShxZI0MdxMeBz": { const { isExpanded, bubbleWidth, bubbleHeight } = event.data; const previousExpanded = bubbleState.isExpanded; bubbleState = { isExpanded, bubbleWidth, bubbleHeight }; if (isExpanded && !previousExpanded) { // Expanding: resize immediately so iframe is big enough for animation calculateAndSetIframeDimensions(true); } else if (!isExpanded && previousExpanded) { // Collapsing: delay resize until animation completes calculateAndSetIframeDimensions(false); } else { // Size change while in same state (e.g., bubble width change) calculateAndSetIframeDimensions(true); } break; } case "FOURTEEN_IFRAME_SHOW_sphShxZI0MdxMeBz": { iframe.style.display = "block"; break; } case "FOURTEEN_IFRAME_SET_POSITION_sphShxZI0MdxMeBz_bottom-left": { iframe.style.right = "unset"; iframe.style.left = "0px"; break; } case "FOURTEEN_IFRAME_SET_POSITION_sphShxZI0MdxMeBz_bottom-right": { iframe.style.right = "0px"; iframe.style.left = "unset"; break; } case "FOURTEEN_IFRAME_HIDE_sphShxZI0MdxMeBz": { iframe.style.display = "none"; break; } default: { break; } } }); }; if (document.readyState === "complete") { load(); } else { document.addEventListener("readystatechange", () => { if (document.readyState === "complete") { load(); } }); } }, setMetadata: (metadata) => { sendMessage({ metadata }); }, setUser: (emailOrObject, name) => { if (typeof emailOrObject === 'string') { sendMessage({ type: 'FOURTEEN_SET_USER', user: { email: emailOrObject, name } }); } else { sendMessage({ type: 'FOURTEEN_SET_USER', user: emailOrObject }); } }, setUserSecure: (encrypted) => { sendMessage({ type: 'FOURTEEN_SET_USER_SECURE', user: { encrypted } }); }, openPopup: () => { sendMessage({ type: 'FOURTEEN_EMBED_OPEN_POPUP_sphShxZI0MdxMeBz' }); }, closePopup: () => { sendMessage({ type: 'FOURTEEN_EMBED_CLOSE_POPUP_sphShxZI0MdxMeBz' }); }, onOpenChange: (callback) => { onOpenChangeCallback = callback; }, openOpenChange: (callback) => { onOpenChangeCallback = callback; }, onNotificationsChange: (callback) => { onNotificationsChangeCallback = callback; }, onReady: (callback) => { if (isEmbedReady) { callback(); } else { onReadyCallbacks.push(callback); } } }; }; // Unique instance window.fourteenEmbed_sphShxZI0MdxMeBz = window.fourteenEmbed_sphShxZI0MdxMeBz || getConfig(); // If this is the first instance, also assign it to the global window.fourteen if (!window.fourteen) { window.fourteen = window.fourteenEmbed_sphShxZI0MdxMeBz; } // Initialize the widget window.fourteenEmbed_sphShxZI0MdxMeBz.init(); })();