(function () { // Retrieve script configuration let currentScript = document.currentScript; if (!currentScript) { currentScript = document.getElementById('kartabot-widget-script') || document.getElementById('kartabot-preview-script'); } if (!currentScript) { // Search for any script tag containing 'widget.js' in its src const scripts = document.getElementsByTagName('script'); for (let i = 0; i < scripts.length; i++) { const src = scripts[i].getAttribute('src') || ''; if (src && (src.includes('widget.js') || scripts[i].id.includes('kartabot'))) { currentScript = scripts[i]; break; } } } // Prevent duplicate script execution (skip for live preview in dashboard console) const isPreview = currentScript && currentScript.id === 'kartabot-preview-script'; if (!isPreview) { if (window.KartaBotWidgetInitialized) return; window.KartaBotWidgetInitialized = true; } // Fallback defaults if no script tag is resolved const botName = currentScript ? (currentScript.getAttribute('data-bot-name') || 'KartaBot') : 'KartaBot'; const accentColor = currentScript ? (currentScript.getAttribute('data-color') || '#6366f1') : '#6366f1'; const theme = currentScript ? (currentScript.getAttribute('data-theme') || 'light') : 'light'; const welcomeMessage = currentScript ? (currentScript.getAttribute('data-welcome') || 'Hello! How can I help you today?') : 'Hello! How can I help you today?'; const avatarUrl = currentScript ? (currentScript.getAttribute('data-avatar') || '') : ''; const position = currentScript ? (currentScript.getAttribute('data-position') || 'right') : 'right'; const n8nUrl = currentScript ? (currentScript.getAttribute('data-n8n-url') || '') : ''; const suggestions = currentScript ? (currentScript.getAttribute('data-suggestions') || '') : ''; // Resolve widget directory to load widget-ui.html relative to this script let baseUrl = ''; try { if (currentScript && currentScript.src) { const scriptSrc = currentScript.src; const url = new URL(scriptSrc); baseUrl = url.origin + url.pathname.substring(0, url.pathname.lastIndexOf('/')); } } catch (e) { console.error('KartaBot: Error resolving script path. Using relative path.', e); } // If baseUrl couldn't be resolved or is empty, use the current document origin if (!baseUrl) { baseUrl = window.location.origin + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')); } const widgetUiUrl = `${baseUrl}/widget-ui.html`; // Construct iframe source URL with configuration query parameters const queryParams = new URLSearchParams({ botName: botName, color: accentColor, theme: theme, welcome: welcomeMessage, }); if (avatarUrl) queryParams.set('avatar', avatarUrl); if (n8nUrl) queryParams.set('n8nUrl', n8nUrl); if (suggestions) queryParams.set('suggestions', suggestions); const finalIframeUrl = `${widgetUiUrl}?${queryParams.toString()}`; // Inject styles for launcher and iframe container const styleEl = document.createElement('style'); const positionStyles = position === 'left' ? `left: 20px; right: auto;` : `right: 20px; left: auto;`; const flexPositionStyles = position === 'left' ? `align-items: flex-start;` : `align-items: flex-end;`; styleEl.textContent = ` .kartabot-widget-container { position: fixed; bottom: 20px; ${positionStyles} z-index: 999999; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; display: flex; flex-direction: column; ${flexPositionStyles} gap: 16px; } /* Nesting inside preview container override */ .kartabot-widget-container.kartabot-inline { position: absolute; bottom: 20px; z-index: 99999; } .kartabot-iframe-wrapper { width: 380px; height: 600px; max-height: calc(100vh - 110px); background: transparent; box-shadow: 0 12px 40px rgba(0, 0, 0, 0.16); border-radius: 20px; overflow: hidden; border: 1px solid rgba(0, 0, 0, 0.05); transform: translateY(20px) scale(0.95); opacity: 0; pointer-events: none; transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.25s ease; transform-origin: bottom ${position}; } .kartabot-widget-container.kartabot-inline .kartabot-iframe-wrapper { max-height: calc(100% - 80px); height: 480px; width: 340px; } .kartabot-iframe-wrapper.open { transform: translateY(0) scale(1); opacity: 1; pointer-events: auto; } .kartabot-iframe-wrapper iframe { width: 100%; height: 100%; border: none; display: block; } .kartabot-launcher { width: 56px; height: 56px; border-radius: 50%; background-color: ${accentColor}; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.25s ease; position: relative; } .kartabot-launcher:hover { transform: scale(1.08); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22); } .kartabot-launcher:active { transform: scale(0.95); } .kartabot-launcher-icon { position: absolute; transition: transform 0.25s ease, opacity 0.2s ease; color: #ffffff; display: flex; align-items: center; justify-content: center; } .kartabot-icon-chat { opacity: 1; transform: rotate(0deg) scale(1); } .kartabot-icon-close { opacity: 0; transform: rotate(-90deg) scale(0.5); } .kartabot-launcher.open .kartabot-icon-chat { opacity: 0; transform: rotate(90deg) scale(0.5); } .kartabot-launcher.open .kartabot-icon-close { opacity: 1; transform: rotate(0deg) scale(1); } /* Mobile Responsiveness */ @media (max-width: 480px) { .kartabot-widget-container:not(.kartabot-inline) { bottom: 0; left: 0; right: 0; padding: 0; gap: 0; } .kartabot-widget-container:not(.kartabot-inline) .kartabot-iframe-wrapper { width: 100vw; height: 100vh; max-height: 100vh; border-radius: 0; bottom: 0; left: 0; right: 0; position: fixed; border: none; transform: translateY(100%); transform-origin: bottom center; } .kartabot-widget-container:not(.kartabot-inline) .kartabot-iframe-wrapper.open { transform: translateY(0); } .kartabot-widget-container:not(.kartabot-inline) .kartabot-launcher { position: fixed; bottom: 20px; ${positionStyles} z-index: 1000000; transition: transform 0.25s ease; } .kartabot-widget-container:not(.kartabot-inline) .kartabot-launcher.open { transform: scale(0); pointer-events: none; } } `; document.head.appendChild(styleEl); // Check if a specific preview host element exists const host = document.getElementById('kartabot-widget-host') || document.body; // Create markup const widgetContainer = document.createElement('div'); widgetContainer.className = 'kartabot-widget-container'; if (host !== document.body) { widgetContainer.classList.add('kartabot-inline'); } // Iframe Wrapper const iframeWrapper = document.createElement('div'); iframeWrapper.className = 'kartabot-iframe-wrapper'; const iframe = document.createElement('iframe'); iframe.src = finalIframeUrl; iframe.title = botName; iframe.allow = 'autoplay; microphone; clipboard-write'; iframeWrapper.appendChild(iframe); widgetContainer.appendChild(iframeWrapper); // Launcher Button const launcher = document.createElement('button'); launcher.className = 'kartabot-launcher'; launcher.setAttribute('aria-label', `Open chat with ${botName}`); // SVG Chat Icon const chatIcon = document.createElement('div'); chatIcon.className = 'kartabot-launcher-icon kartabot-icon-chat'; chatIcon.innerHTML = ` `; // SVG Close Icon const closeIcon = document.createElement('div'); closeIcon.className = 'kartabot-launcher-icon kartabot-icon-close'; closeIcon.innerHTML = ` `; launcher.appendChild(chatIcon); launcher.appendChild(closeIcon); widgetContainer.appendChild(launcher); // Append to host container host.appendChild(widgetContainer); // Toggle Functionality function toggleChat() { const isOpen = iframeWrapper.classList.toggle('open'); launcher.classList.toggle('open', isOpen); if (isOpen) { // Focus the chat input inside the iframe if possible try { iframe.contentWindow.postMessage({ type: 'kartabot-widget-focus' }, '*'); } catch (e) {} } } launcher.addEventListener('click', toggleChat); // Handle postMessage commands from inside iframe (like Close button) window.addEventListener('message', (event) => { // Basic verification: accept toggles from any source for template compatibility if (event.data && event.data.type === 'kartabot-widget-toggle') { if (event.data.action === 'close') { iframeWrapper.classList.remove('open'); launcher.classList.remove('open'); } } }); })();