(function() { 'use strict'; const config = window.AIChatConfig || {}; const orgId = config.organizationId; const apiKey = config.apiKey; if (!orgId || !apiKey) { console.error('AIChatWidget: organizationId and apiKey required'); return; } // Check page visibility - if visiblePages is set, only show on exact matching pages const visiblePages = config.visiblePages || []; if (visiblePages.length > 0) { const currentPath = window.location.pathname; // Remove query string and hash for clean URL comparison const currentUrlClean = window.location.origin + window.location.pathname; const isVisible = visiblePages.some(function(page) { // Normalize: remove trailing slashes for comparison const normalizedPage = page.replace(/\/+$/, ''); // If page starts with http:// or https://, match against full URL (exact match) if (page.startsWith('http://') || page.startsWith('https://')) { const normalizedCurrentUrl = currentUrlClean.replace(/\/+$/, ''); return normalizedCurrentUrl === normalizedPage; } // Otherwise, treat as path and match against pathname (exact match) const normalizedPath = currentPath.replace(/\/+$/, ''); // Handle root path special case if (normalizedPage === '' && normalizedPath === '') { return true; } return normalizedPath === normalizedPage; }); if (!isVisible) { console.log('[Widget] Page not in visiblePages list, not showing widget. Current path:', currentPath); return; } } let iframe = null; let isOpen = false; let isInitialized = false; let isFullscreen = false; let iframeReady = false; let pendingMessages = []; // Create pre-chat messages and CTA bubbles container const preChatMessages = config.preChatMessages || []; const ctaButton = config.ctaButton || { enabled: false, text: '', formId: '' }; let bubblesContainer = null; const dismissKey = 'ai_chat_prechat_dismissed_' + orgId; const isDismissed = localStorage.getItem(dismissKey) === 'true'; const hasContent = preChatMessages.length > 0 || (ctaButton.enabled && ctaButton.text && ctaButton.formId); if (hasContent && !isDismissed) { bubblesContainer = document.createElement('div'); bubblesContainer.style.cssText = ` position: fixed; ${config.position === 'bottom-left' ? 'left: 20px' : 'right: 20px'}; bottom: 95px; display: flex; flex-direction: column; gap: 8px; z-index: 999999; max-width: 280px; `; // Single dismiss button for all messages and CTA const dismissBtn = document.createElement('button'); dismissBtn.innerHTML = ` `; dismissBtn.style.cssText = ` position: absolute; top: -8px; ${config.position === 'bottom-left' ? 'right: -8px' : 'right: -8px'}; width: 22px; height: 22px; border: none; background: white; cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center; color: #9ca3af; border-radius: 50%; box-shadow: 0 2px 6px rgba(0,0,0,0.15); transition: color 0.2s, background 0.2s, transform 0.2s; z-index: 1; `; dismissBtn.onmouseover = () => { dismissBtn.style.color = '#6b7280'; dismissBtn.style.background = '#f9fafb'; dismissBtn.style.transform = 'scale(1.1)'; }; dismissBtn.onmouseout = () => { dismissBtn.style.color = '#9ca3af'; dismissBtn.style.background = 'white'; dismissBtn.style.transform = 'scale(1)'; }; dismissBtn.onclick = (e) => { e.stopPropagation(); localStorage.setItem(dismissKey, 'true'); if (bubblesContainer) { bubblesContainer.style.opacity = '0'; bubblesContainer.style.transform = 'translateY(10px)'; bubblesContainer.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out'; setTimeout(() => { if (bubblesContainer && bubblesContainer.parentNode) { bubblesContainer.parentNode.removeChild(bubblesContainer); } }, 300); } }; bubblesContainer.appendChild(dismissBtn); // Add pre-chat message bubbles preChatMessages.forEach((message, index) => { const bubble = document.createElement('div'); bubble.style.cssText = ` background: white; color: #1f2937; padding: 12px 16px; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 14px; line-height: 1.4; opacity: 0; transform: translateY(10px); animation: slideUp 0.4s ease-out ${index * 0.15}s forwards; `; bubble.textContent = message; bubblesContainer.appendChild(bubble); }); // Add CTA button if enabled if (ctaButton.enabled && ctaButton.text && ctaButton.formId) { const ctaBtn = document.createElement('button'); ctaBtn.textContent = ctaButton.text; ctaBtn.style.cssText = ` background: ${config.primaryColor || '#046eff'}; color: white; padding: 10px 16px; border-radius: 20px; border: none; box-shadow: 0 4px 12px rgba(0,0,0,0.15); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 14px; font-weight: 500; line-height: 1.4; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; `; ctaBtn.onmouseover = () => { ctaBtn.style.transform = 'scale(1.02)'; ctaBtn.style.boxShadow = '0 6px 16px rgba(0,0,0,0.2)'; }; ctaBtn.onmouseout = () => { ctaBtn.style.transform = 'scale(1)'; ctaBtn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; }; ctaBtn.onclick = (e) => { e.stopPropagation(); if (window.AIChatWidget && window.AIChatWidget.openForm) { window.AIChatWidget.openForm(ctaButton.formId); } }; bubblesContainer.appendChild(ctaBtn); } } // Create floating button (no server calls) const button = document.createElement('button'); button.setAttribute('aria-label', 'Open chat'); // Determine button size const buttonSize = config.buttonSize || 'medium'; const sizeMap = { 'very-small': { size: '40px', iconSize: '18' }, small: { size: '48px', iconSize: '20' }, medium: { size: '60px', iconSize: '24' }, large: { size: '72px', iconSize: '28' }, 'very-large': { size: '84px', iconSize: '32' } }; const { size, iconSize } = sizeMap[buttonSize] || sizeMap.medium; button.innerHTML = ` `; button.style.cssText = ` position: fixed; ${config.position === 'bottom-left' ? 'left: 20px' : 'right: 20px'}; bottom: 20px; width: ${size}; height: ${size}; border-radius: 50%; background: ${config.primaryColor || '#046eff'}; border: none; cursor: pointer; box-shadow: 0 4px 12px rgba(0,0,0,0.15); color: white; display: flex; align-items: center; justify-content: center; z-index: 999999; transition: transform 0.2s, box-shadow 0.2s; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; `; button.onmouseover = () => { button.style.transform = 'scale(1.1)'; button.style.boxShadow = '0 6px 16px rgba(0,0,0,0.2)'; }; button.onmouseout = () => { button.style.transform = 'scale(1)'; button.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; }; // Initialize chat ONLY when button is clicked button.onclick = (e) => { e.stopPropagation(); if (!isInitialized) { initializeChat(); isInitialized = true; } toggleChat(); }; // Lazy initialization - creates iframe on first click function initializeChat() { console.log('[Widget] initializeChat() called'); // Create iframe container const container = document.createElement('div'); container.style.cssText = ` position: fixed; ${config.position === 'bottom-left' ? 'left: 20px' : 'right: 20px'}; bottom: 90px; width: 400px; height: 600px; max-width: calc(100vw - 40px); max-height: calc(100vh - 120px); border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.2); overflow: hidden; display: none; z-index: 999998; background: white; border: 1px solid rgba(0,0,0,0.1); `; // Create iframe - THIS IS THE FIRST SERVER REQUEST iframe = document.createElement('iframe'); const baseUrl = config.baseUrl || window.location.origin; const showBranding = config.showBranding !== false ? 'true' : 'false'; const showMenuButtons = config.showMenuButtons !== false ? 'true' : 'false'; const params = new URLSearchParams({ apiKey: apiKey, color: config.primaryColor || '#046eff', showBranding: showBranding, showMenuButtons: showMenuButtons, headerBg: config.headerBackgroundColor || '#ffffff', chatBg: config.chatBackgroundColor || '#f9fafb', titleColor: config.titleColor || '#046eff', userMsgColor: config.userMessageColor || '#046eff', assistantMsgColor: config.assistantMessageColor || '#f3f4f6', title: config.title || 'Chat Assistant' }); iframe.src = `${baseUrl}/embed/chat/${orgId}?${params.toString()}`; iframe.style.cssText = ` width: 100%; height: 100%; border: none; border-radius: 12px; `; iframe.allow = 'clipboard-write'; iframe.title = 'Chat widget'; container.appendChild(iframe); document.body.appendChild(container); // Store reference for toggling window.__aiChatContainer = container; console.log('[Widget] Container created and stored:', container); } function toggleChat() { console.log('[Widget] toggleChat() called, isOpen before:', isOpen); const container = window.__aiChatContainer; if (!container) { console.error('[Widget] Container not found in toggleChat!'); return; } isOpen = !isOpen; console.log('[Widget] isOpen after toggle:', isOpen); container.style.display = isOpen ? 'block' : 'none'; console.log('[Widget] Container display set to:', container.style.display); // Toggle pre-chat bubbles visibility (swirl keeps animating) if (bubblesContainer) { bubblesContainer.style.display = isOpen ? 'none' : 'flex'; } // Update button icon button.innerHTML = isOpen ? ` ` : ` `; } // Add CSS animations for bubbles and button swirl const style = document.createElement('style'); const buttonAnimation = config.buttonAnimation || { enabled: false, secondaryColor: '#ff6b6b' }; const primaryColor = config.primaryColor || '#046eff'; const secondaryColor = buttonAnimation.secondaryColor || '#ff6b6b'; style.textContent = ` @keyframes slideUp { to { opacity: 1; transform: translateY(0); } } @keyframes lavaBlob1 { 0%, 100% { transform: translate(0%, 0%) scale(1); } 25% { transform: translate(30%, 20%) scale(1.1); } 50% { transform: translate(10%, 40%) scale(0.9); } 75% { transform: translate(-20%, 15%) scale(1.05); } } @keyframes lavaBlob2 { 0%, 100% { transform: translate(0%, 0%) scale(1); } 25% { transform: translate(-25%, 30%) scale(0.95); } 50% { transform: translate(20%, -20%) scale(1.1); } 75% { transform: translate(35%, 25%) scale(1); } } @keyframes lavaBlob3 { 0%, 100% { transform: translate(0%, 0%) scale(1.05); } 33% { transform: translate(-30%, -25%) scale(0.9); } 66% { transform: translate(25%, 30%) scale(1.1); } } .ai-lava-container { position: fixed; border-radius: 50%; pointer-events: none; z-index: 999998; overflow: hidden; background: ${primaryColor}; } .ai-lava-blob { position: absolute; border-radius: 50%; filter: blur(8px); } .ai-lava-blob-1 { width: 70%; height: 70%; top: 10%; left: 10%; background: ${secondaryColor}; animation: lavaBlob1 3s ease-in-out infinite; } .ai-lava-blob-2 { width: 60%; height: 60%; top: 25%; left: 25%; background: ${primaryColor}; animation: lavaBlob2 4s ease-in-out infinite; } .ai-lava-blob-3 { width: 50%; height: 50%; top: 20%; left: 30%; background: ${secondaryColor}; opacity: 0.8; animation: lavaBlob3 5s ease-in-out infinite; } .ai-chat-button-animated { background: transparent !important; box-shadow: none !important; border: none !important; } .ai-chat-button-animated:hover { box-shadow: none !important; border: none !important; transform: scale(1.1); } `; document.head.appendChild(style); // Apply lava lamp animation to button if enabled let lavaElement = null; if (buttonAnimation.enabled) { lavaElement = document.createElement('div'); lavaElement.className = 'ai-lava-container'; lavaElement.style.cssText = ` ${config.position === 'bottom-left' ? 'left: 20px' : 'right: 20px'}; bottom: 20px; width: ${size}; height: ${size}; `; // Create the blobs const blob1 = document.createElement('div'); blob1.className = 'ai-lava-blob ai-lava-blob-1'; const blob2 = document.createElement('div'); blob2.className = 'ai-lava-blob ai-lava-blob-2'; const blob3 = document.createElement('div'); blob3.className = 'ai-lava-blob ai-lava-blob-3'; lavaElement.appendChild(blob1); lavaElement.appendChild(blob2); lavaElement.appendChild(blob3); // Make the main button transparent so lava shows through button.classList.add('ai-chat-button-animated'); button.style.zIndex = '1000000'; } // Add button, bubbles, and lava animation to page when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { if (lavaElement) { document.body.appendChild(lavaElement); } if (bubblesContainer) { document.body.appendChild(bubblesContainer); } document.body.appendChild(button); }); } else { if (lavaElement) { document.body.appendChild(lavaElement); } if (bubblesContainer) { document.body.appendChild(bubblesContainer); } document.body.appendChild(button); } // Note: Widget only closes when close button is clicked // No outside click or escape key handling // Function to send message to iframe (queues if not ready) function sendMessageToIframe(message) { if (iframeReady && iframe && iframe.contentWindow) { console.log('[Widget] Sending message to iframe:', message); iframe.contentWindow.postMessage(message, '*'); } else { console.log('[Widget] Iframe not ready, queueing message:', message); pendingMessages.push(message); } } // Process any pending messages function processPendingMessages() { if (pendingMessages.length > 0 && iframe && iframe.contentWindow) { console.log('[Widget] Processing', pendingMessages.length, 'pending messages'); pendingMessages.forEach(function(message) { console.log('[Widget] Sending queued message:', message); iframe.contentWindow.postMessage(message, '*'); }); pendingMessages = []; } } // Listen for messages from iframe window.addEventListener('message', (event) => { console.log('Widget received message:', event.data); if (!event.data || typeof event.data !== 'object') return; if (event.data.type === 'AI_CHAT_FULLSCREEN') { console.log('Toggling fullscreen to:', event.data.fullscreen); toggleFullscreen(event.data.fullscreen); } // Handle iframe ready signal if (event.data.type === 'AI_CHAT_IFRAME_READY') { console.log('[Widget] Iframe signaled ready'); iframeReady = true; processPendingMessages(); } }); function toggleFullscreen(shouldBeFullscreen) { console.log('toggleFullscreen called with:', shouldBeFullscreen); const container = window.__aiChatContainer; console.log('Container:', container); if (!container) { console.error('Container not found!'); return; } isFullscreen = shouldBeFullscreen; if (isFullscreen) { console.log('Applying fullscreen styles'); // Fullscreen mode container.style.cssText = ` position: fixed; left: 0; top: 0; right: 0; bottom: 0; width: 100vw; height: 100vh; max-width: 100vw; max-height: 100vh; border-radius: 0; box-shadow: none; display: block; z-index: 999998; background: white; border: none; `; } else { console.log('Applying normal styles'); // Normal mode container.style.cssText = ` position: fixed; ${config.position === 'bottom-left' ? 'left: 20px' : 'right: 20px'}; bottom: 90px; width: 400px; height: 600px; max-width: calc(100vw - 40px); max-height: calc(100vh - 120px); border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.2); overflow: hidden; display: block; z-index: 999998; background: white; border: 1px solid rgba(0,0,0,0.1); `; } } // Expose API for programmatic control window.AIChatWidget = { open: function() { if (!isInitialized) { initializeChat(); isInitialized = true; } if (!isOpen) { toggleChat(); } }, close: function() { if (isOpen) { toggleChat(); } }, openForm: function(formId) { console.log('[Widget] openForm() called with formId:', formId); console.log('[Widget] isInitialized:', isInitialized, 'isOpen:', isOpen, 'iframeReady:', iframeReady); if (!isInitialized) { console.log('[Widget] Initializing chat...'); initializeChat(); isInitialized = true; } if (!isOpen) { console.log('[Widget] Chat is closed, calling toggleChat()...'); toggleChat(); } else { console.log('[Widget] Chat is already open'); } // Send message (will be queued if iframe not ready) sendMessageToIframe({ type: 'AI_CHAT_OPEN_FORM', formId: formId }); }, openHelpCenter: function() { console.log('[Widget] openHelpCenter() called'); if (!isInitialized) { initializeChat(); isInitialized = true; } if (!isOpen) { toggleChat(); } // Send message (will be queued if iframe not ready) sendMessageToIframe({ type: 'AI_CHAT_OPEN_HELP_CENTER' }); }, openFeedbackCenter: function() { console.log('[Widget] openFeedbackCenter() called'); if (!isInitialized) { initializeChat(); isInitialized = true; } if (!isOpen) { toggleChat(); } // Send message (will be queued if iframe not ready) sendMessageToIframe({ type: 'AI_CHAT_OPEN_FEEDBACK_CENTER' }); } }; })();