| |
| const hamburger = document.querySelector('.hamburger'); |
| const navMenu = document.querySelector('.nav-menu'); |
|
|
| if (hamburger) { |
| hamburger.addEventListener('click', () => { |
| navMenu.classList.toggle('active'); |
| hamburger.classList.toggle('active'); |
| }); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| function initSubmenuPositioning() { |
| |
| |
| const topLevelDropdowns = document.querySelectorAll('.nav-menu > .dropdown'); |
| |
| topLevelDropdowns.forEach(parentDropdown => { |
| const parentLink = parentDropdown.querySelector('a.nav-link'); |
| if (!parentLink) return; |
| |
| |
| const linkText = parentLink.textContent.trim(); |
| const linkHref = parentLink.getAttribute('href') || ''; |
| let parentType = null; |
| |
| if (linkText.includes('In') || linkHref.includes('/in.html') || linkHref.includes('/in/in.html')) { |
| parentType = 'in'; |
| } else if (linkText.includes('Proc') || linkHref.includes('/processing.html') || linkHref.includes('/processing/processing.html')) { |
| parentType = 'processing'; |
| } else if (linkText.includes('Out') || linkHref.includes('/out.html') || linkHref.includes('/out/out.html')) { |
| parentType = 'out'; |
| } |
| |
| |
| const parentDropdownMenu = parentDropdown.querySelector('.dropdown-menu'); |
| if (!parentDropdownMenu) return; |
| |
| |
| function determineParentDirection() { |
| const parentRect = parentDropdown.getBoundingClientRect(); |
| const viewportWidth = window.innerWidth; |
| const padding = 10; |
| const safetyMargin = 20; |
| const rightMargin = viewportWidth - padding - safetyMargin; |
| |
| |
| let maxDepth = 0; |
| function findMaxDepth(element, currentDepth = 0) { |
| |
| const directLiChildren = Array.from(element.children).filter(child => child.tagName === 'LI'); |
| const childMenus = []; |
| directLiChildren.forEach(li => { |
| const directUlChild = Array.from(li.children).find(child => child.tagName === 'UL'); |
| if (directUlChild) { |
| childMenus.push(directUlChild); |
| } |
| }); |
| |
| if (childMenus.length === 0) { |
| if (currentDepth > maxDepth) { |
| maxDepth = currentDepth; |
| } |
| return; |
| } |
| childMenus.forEach(childMenu => { |
| findMaxDepth(childMenu, currentDepth + 1); |
| }); |
| } |
| findMaxDepth(parentDropdownMenu); |
| |
| |
| const estimatedSubmenuWidth = 220; |
| |
| |
| |
| const rightmostEdgePosition = parentRect.right + (estimatedSubmenuWidth * maxDepth); |
| |
| |
| console.log(`[Menu Direction] ${parentType || 'unknown'}: parent.right=${parentRect.right.toFixed(0)}, maxDepth=${maxDepth}, rightmostEdge=${rightmostEdgePosition.toFixed(0)}, rightMargin=${rightMargin.toFixed(0)}`); |
| |
| |
| if (rightmostEdgePosition >= rightMargin) { |
| |
| console.log(`[Menu Direction] ${parentType || 'unknown'}: OVERFLOW DETECTED → pointing LEFTWARD`); |
| return 'leftward'; |
| } |
| |
| |
| const allChildMenus = parentDropdownMenu.querySelectorAll('ul'); |
| for (let childMenu of allChildMenus) { |
| |
| let depth = 0; |
| let current = childMenu; |
| while (current && current !== parentDropdownMenu) { |
| if (current.tagName === 'UL') { |
| depth++; |
| } |
| current = current.parentElement; |
| } |
| |
| |
| const parentMenuItem = childMenu.parentElement; |
| if (parentMenuItem) { |
| const menuItemRect = parentMenuItem.getBoundingClientRect(); |
| |
| const estimatedRightEdge = menuItemRect.right + estimatedSubmenuWidth; |
| |
| |
| if (estimatedRightEdge >= rightMargin) { |
| console.log(`[Menu Direction] ${parentType || 'unknown'}: Individual menu overflow → pointing LEFTWARD`); |
| return 'leftward'; |
| } |
| } |
| } |
| |
| |
| if (parentType === 'out' || parentType === 'processing') { |
| const spaceOnRight = viewportWidth - parentRect.right - padding - safetyMargin; |
| const spaceOnLeft = parentRect.left - padding; |
| |
| |
| if (parentRect.right > viewportWidth * 0.7 || spaceOnRight < spaceOnLeft * 1.2) { |
| console.log(`[Menu Direction] ${parentType}: Close to right edge → pointing LEFTWARD`); |
| return 'leftward'; |
| } |
| } |
| |
| |
| console.log(`[Menu Direction] ${parentType || 'unknown'}: Enough space → pointing RIGHTWARD`); |
| return 'rightward'; |
| } |
| |
| |
| const parentDirection = determineParentDirection(); |
| |
| |
| const allChildMenus = parentDropdownMenu.querySelectorAll('ul'); |
| |
| |
| allChildMenus.forEach(childMenu => { |
| if (parentDirection === 'leftward') { |
| |
| childMenu.classList.add('submenu-reversed'); |
| childMenu.style.setProperty('left', 'auto', 'important'); |
| childMenu.style.setProperty('right', '100%', 'important'); |
| childMenu.style.setProperty('margin-left', '0', 'important'); |
| childMenu.style.setProperty('margin-right', '2px', 'important'); |
| } else { |
| |
| childMenu.classList.remove('submenu-reversed'); |
| childMenu.style.setProperty('left', '100%', 'important'); |
| childMenu.style.setProperty('right', 'auto', 'important'); |
| childMenu.style.setProperty('margin-left', '2px', 'important'); |
| childMenu.style.setProperty('margin-right', '0', 'important'); |
| } |
| }); |
| |
| |
| const menuItemsWithChildren = parentDropdownMenu.querySelectorAll('li:has(> ul)'); |
| |
| menuItemsWithChildren.forEach(menuItem => { |
| |
| const submenu = Array.from(menuItem.children).find(child => child.tagName === 'UL'); |
| if (!submenu) return; |
| |
| |
| menuItem.addEventListener('mouseenter', () => { |
| if (parentDirection === 'leftward') { |
| submenu.classList.add('submenu-reversed'); |
| submenu.style.setProperty('left', 'auto', 'important'); |
| submenu.style.setProperty('right', '100%', 'important'); |
| submenu.style.setProperty('margin-left', '0', 'important'); |
| submenu.style.setProperty('margin-right', '2px', 'important'); |
| } else { |
| submenu.classList.remove('submenu-reversed'); |
| submenu.style.setProperty('left', '100%', 'important'); |
| submenu.style.setProperty('right', 'auto', 'important'); |
| submenu.style.setProperty('margin-left', '2px', 'important'); |
| submenu.style.setProperty('margin-right', '0', 'important'); |
| } |
| }); |
| }); |
| }); |
| |
| |
| let resizeTimer; |
| window.addEventListener('resize', () => { |
| clearTimeout(resizeTimer); |
| resizeTimer = setTimeout(() => { |
| initSubmenuPositioning(); |
| }, 100); |
| }); |
| } |
|
|
| |
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', initSubmenuPositioning); |
| } else { |
| initSubmenuPositioning(); |
| } |
|
|
| |
| setTimeout(initSubmenuPositioning, 600); |
| setTimeout(initSubmenuPositioning, 1000); |
|
|
| |
| |
| const observer = new MutationObserver((mutations) => { |
| |
| let shouldUpdate = false; |
| mutations.forEach(mutation => { |
| mutation.addedNodes.forEach(node => { |
| if (node.nodeType === 1 && ( |
| node.classList?.contains('dropdown') || |
| node.classList?.contains('dropdown-menu') || |
| node.querySelector?.('.dropdown') || |
| node.querySelector?.('.dropdown-menu') |
| )) { |
| shouldUpdate = true; |
| } |
| }); |
| }); |
| if (shouldUpdate) { |
| setTimeout(initSubmenuPositioning, 100); |
| } |
| }); |
| observer.observe(document.body, { childList: true, subtree: true }); |
|
|
| |
| const navLinks = document.querySelectorAll('.nav-link, .dropdown-link, .submenu-link'); |
| navLinks.forEach(link => { |
| link.addEventListener('click', () => { |
| navMenu.classList.remove('active'); |
| hamburger.classList.remove('active'); |
| }); |
| }); |
|
|
| |
| const dropdowns = document.querySelectorAll('.dropdown'); |
| dropdowns.forEach(dropdown => { |
| const dropdownLink = dropdown.querySelector('.nav-link'); |
| if (dropdownLink) { |
| dropdownLink.addEventListener('click', (e) => { |
| if (window.innerWidth <= 768) { |
| e.preventDefault(); |
| dropdown.classList.toggle('active'); |
| } |
| }); |
| } |
| }); |
|
|
| |
| |
| (function() { |
| if (typeof localStorage !== 'undefined') { |
| const savedTheme = localStorage.getItem('theme') || 'light'; |
| if (savedTheme === 'dark') { |
| |
| const darkThemeStyle = document.createElement('style'); |
| darkThemeStyle.id = 'dark-theme-override-style'; |
| darkThemeStyle.textContent = ` |
| /* Override CSS variables at root level */ |
| :root.dark-theme, |
| html.dark-theme, |
| body.dark-theme { |
| --color-text: #ffffff !important; |
| --color-text-light: #ffffff !important; |
| } |
| |
| /* Force all content-text and children to white */ |
| body.dark-theme .content-text, |
| html.dark-theme .content-text, |
| body.dark-theme .content-text *, |
| html.dark-theme .content-text *, |
| body.dark-theme .content-text p, |
| html.dark-theme .content-text p, |
| body.dark-theme .section .content-text, |
| html.dark-theme .section .content-text, |
| body.dark-theme .section .content-text *, |
| html.dark-theme .section .content-text *, |
| body.dark-theme .section .content-text p, |
| html.dark-theme .section .content-text p, |
| body.dark-theme main .content-text, |
| html.dark-theme main .content-text, |
| body.dark-theme main .content-text *, |
| html.dark-theme main .content-text *, |
| body.dark-theme main .content-text p, |
| html.dark-theme main .content-text p, |
| body.dark-theme .container .content-text, |
| html.dark-theme .container .content-text, |
| body.dark-theme .container .content-text *, |
| html.dark-theme .container .content-text *, |
| body.dark-theme .container .content-text p, |
| html.dark-theme .container .content-text p, |
| body.dark-theme .content-grid .content-text, |
| html.dark-theme .content-grid .content-text, |
| body.dark-theme .content-grid .content-text *, |
| html.dark-theme .content-grid .content-text *, |
| body.dark-theme .content-grid .content-text p, |
| html.dark-theme .content-grid .content-text p { |
| color: #ffffff !important; |
| --color-text: #ffffff !important; |
| --color-text-light: #ffffff !important; |
| } |
| |
| /* Force background colors */ |
| body.dark-theme main, |
| html.dark-theme main, |
| body.dark-theme .section, |
| html.dark-theme .section, |
| body.dark-theme .container, |
| html.dark-theme .container, |
| body.dark-theme .content-grid, |
| html.dark-theme .content-grid { |
| background-color: #1a1a1a !important; |
| color: #ffffff !important; |
| --color-text: #ffffff !important; |
| --color-text-light: #ffffff !important; |
| } |
| |
| /* Force h1 in content-grid */ |
| body.dark-theme .content-grid h1, |
| html.dark-theme .content-grid h1, |
| body.dark-theme .content-text h1, |
| html.dark-theme .content-text h1 { |
| color: #ffffff !important; |
| } |
| `; |
| |
| if (document.head) { |
| document.head.appendChild(darkThemeStyle); |
| } else { |
| document.addEventListener('DOMContentLoaded', function() { |
| if (!document.getElementById('dark-theme-override-style')) { |
| document.head.appendChild(darkThemeStyle); |
| } |
| }); |
| } |
| } |
| } |
| })(); |
|
|
| function applyInlineDarkThemeStyles() { |
| |
| let darkThemeStyle = document.getElementById('dark-theme-override-style'); |
| if (!darkThemeStyle) { |
| darkThemeStyle = document.createElement('style'); |
| darkThemeStyle.id = 'dark-theme-override-style'; |
| |
| document.head.appendChild(darkThemeStyle); |
| } |
| |
| darkThemeStyle.textContent = ` |
| /* Override CSS variables at root level */ |
| :root.dark-theme, |
| html.dark-theme, |
| body.dark-theme { |
| --color-text: #ffffff !important; |
| --color-text-light: #ffffff !important; |
| } |
| |
| /* Force all content-text and children to white */ |
| body.dark-theme .content-text, |
| html.dark-theme .content-text, |
| body.dark-theme .content-text *, |
| html.dark-theme .content-text *, |
| body.dark-theme .content-text p, |
| html.dark-theme .content-text p, |
| body.dark-theme .section .content-text, |
| html.dark-theme .section .content-text, |
| body.dark-theme .section .content-text *, |
| html.dark-theme .section .content-text *, |
| body.dark-theme .section .content-text p, |
| html.dark-theme .section .content-text p, |
| body.dark-theme main .content-text, |
| html.dark-theme main .content-text, |
| body.dark-theme main .content-text *, |
| html.dark-theme main .content-text *, |
| body.dark-theme main .content-text p, |
| html.dark-theme main .content-text p, |
| body.dark-theme .container .content-text, |
| html.dark-theme .container .content-text, |
| body.dark-theme .container .content-text *, |
| html.dark-theme .container .content-text *, |
| body.dark-theme .container .content-text p, |
| html.dark-theme .container .content-text p, |
| body.dark-theme .content-grid .content-text, |
| html.dark-theme .content-grid .content-text, |
| body.dark-theme .content-grid .content-text *, |
| html.dark-theme .content-grid .content-text *, |
| body.dark-theme .content-grid .content-text p, |
| html.dark-theme .content-grid .content-text p { |
| color: #ffffff !important; |
| --color-text: #ffffff !important; |
| --color-text-light: #ffffff !important; |
| } |
| |
| /* Force background colors */ |
| body.dark-theme main, |
| html.dark-theme main, |
| body.dark-theme .section, |
| html.dark-theme .section, |
| body.dark-theme .container, |
| html.dark-theme .container, |
| body.dark-theme .content-grid, |
| html.dark-theme .content-grid { |
| background-color: #1a1a1a !important; |
| color: #ffffff !important; |
| --color-text: #ffffff !important; |
| --color-text-light: #ffffff !important; |
| } |
| |
| /* Force h1 in content-grid */ |
| body.dark-theme .content-grid h1, |
| html.dark-theme .content-grid h1, |
| body.dark-theme .content-text h1, |
| html.dark-theme .content-text h1 { |
| color: #ffffff !important; |
| } |
| `; |
| |
| |
| document.documentElement.style.setProperty('--color-text', '#ffffff', 'important'); |
| document.documentElement.style.setProperty('--color-text-light', '#ffffff', 'important'); |
| document.documentElement.style.setProperty('background-color', '#1a1a1a', 'important'); |
| document.body.style.setProperty('--color-text', '#ffffff', 'important'); |
| document.body.style.setProperty('--color-text-light', '#ffffff', 'important'); |
| document.body.style.setProperty('background-color', '#1a1a1a', 'important'); |
| |
| |
| const contentTextElements = document.querySelectorAll('.content-text'); |
| const main = document.querySelector('main'); |
| const section = document.querySelector('.section'); |
| const container = document.querySelector('.container'); |
| const contentGrid = document.querySelector('.content-grid'); |
| |
| |
| if (main) { |
| main.style.setProperty('background-color', '#1a1a1a', 'important'); |
| main.style.setProperty('color', '#ffffff', 'important'); |
| main.style.setProperty('--color-text', '#ffffff', 'important'); |
| main.style.setProperty('--color-text-light', '#ffffff', 'important'); |
| } |
| if (section) { |
| section.style.setProperty('background-color', '#1a1a1a', 'important'); |
| section.style.setProperty('color', '#ffffff', 'important'); |
| section.style.setProperty('--color-text', '#ffffff', 'important'); |
| section.style.setProperty('--color-text-light', '#ffffff', 'important'); |
| } |
| if (container) { |
| container.style.setProperty('color', '#ffffff', 'important'); |
| container.style.setProperty('--color-text', '#ffffff', 'important'); |
| container.style.setProperty('--color-text-light', '#ffffff', 'important'); |
| } |
| if (contentGrid) { |
| contentGrid.style.setProperty('color', '#ffffff', 'important'); |
| contentGrid.style.setProperty('--color-text', '#ffffff', 'important'); |
| contentGrid.style.setProperty('--color-text-light', '#ffffff', 'important'); |
| } |
| |
| |
| contentTextElements.forEach(el => { |
| |
| el.style.setProperty('--color-text', '#ffffff', 'important'); |
| el.style.setProperty('--color-text-light', '#ffffff', 'important'); |
| el.style.setProperty('color', '#ffffff', 'important'); |
| el.style.setProperty('background-color', 'transparent', 'important'); |
| |
| |
| const paragraphs = el.querySelectorAll('p'); |
| paragraphs.forEach(p => { |
| |
| p.style.setProperty('color', '#ffffff', 'important'); |
| p.style.setProperty('--color-text-light', '#ffffff', 'important'); |
| }); |
| |
| |
| const allChildren = el.querySelectorAll('*:not(p)'); |
| allChildren.forEach(child => { |
| child.style.setProperty('color', '#ffffff', 'important'); |
| }); |
| |
| const headings = el.querySelectorAll('h1, h2, h3, h4, h5, h6'); |
| headings.forEach(h => { |
| h.style.setProperty('color', '#ffffff', 'important'); |
| }); |
| |
| const textElements = el.querySelectorAll('strong, b, a, span, em, i'); |
| textElements.forEach(t => { |
| t.style.setProperty('color', '#ffffff', 'important'); |
| }); |
| }); |
| |
| |
| const h1Elements = document.querySelectorAll('.content-grid h1, .content-text h1'); |
| h1Elements.forEach(h1 => { |
| h1.style.setProperty('color', '#ffffff', 'important'); |
| }); |
| } |
|
|
| function removeInlineDarkThemeStyles() { |
| |
| const darkThemeStyle = document.getElementById('dark-theme-override-style'); |
| if (darkThemeStyle) { |
| darkThemeStyle.remove(); |
| } |
| |
| |
| document.documentElement.style.removeProperty('background-color'); |
| document.documentElement.style.setProperty('background-color', '#ffffff', 'important'); |
| document.body.style.removeProperty('background-color'); |
| document.body.style.setProperty('background-color', '#ffffff', 'important'); |
| |
| |
| document.documentElement.style.removeProperty('--color-text'); |
| document.documentElement.style.removeProperty('--color-text-light'); |
| document.body.style.removeProperty('--color-text'); |
| document.body.style.removeProperty('--color-text-light'); |
| |
| |
| const contentTextElements = document.querySelectorAll('.content-text'); |
| const main = document.querySelector('main'); |
| const section = document.querySelector('.section'); |
| const container = document.querySelector('.container'); |
| const contentGrid = document.querySelector('.content-grid'); |
| |
| |
| if (main) { |
| main.style.setProperty('background-color', '#ffffff', 'important'); |
| main.style.removeProperty('color'); |
| main.style.removeProperty('--color-text'); |
| main.style.removeProperty('--color-text-light'); |
| } |
| if (section) { |
| section.style.setProperty('background-color', '#ffffff', 'important'); |
| section.style.removeProperty('color'); |
| section.style.removeProperty('--color-text'); |
| section.style.removeProperty('--color-text-light'); |
| } |
| if (container) { |
| container.style.removeProperty('color'); |
| container.style.removeProperty('--color-text'); |
| container.style.removeProperty('--color-text-light'); |
| } |
| if (contentGrid) { |
| contentGrid.style.removeProperty('color'); |
| contentGrid.style.removeProperty('--color-text'); |
| contentGrid.style.removeProperty('--color-text-light'); |
| } |
| |
| |
| contentTextElements.forEach(el => { |
| el.style.removeProperty('--color-text'); |
| el.style.removeProperty('--color-text-light'); |
| el.style.removeProperty('color'); |
| el.style.removeProperty('background-color'); |
| |
| |
| const paragraphs = el.querySelectorAll('p'); |
| paragraphs.forEach(p => { |
| p.style.removeProperty('color'); |
| p.style.removeProperty('--color-text-light'); |
| }); |
| |
| |
| const allChildren = el.querySelectorAll('*:not(p)'); |
| allChildren.forEach(child => { |
| child.style.removeProperty('color'); |
| }); |
| }); |
| |
| |
| const h1Elements = document.querySelectorAll('.content-grid h1, .content-text h1'); |
| h1Elements.forEach(h1 => { |
| h1.style.removeProperty('color'); |
| }); |
| } |
|
|
| function initThemeToggle() { |
| const themeToggle = document.getElementById('themeToggle'); |
| const body = document.body; |
| const html = document.documentElement; |
| |
| if (!themeToggle) { |
| |
| setTimeout(initThemeToggle, 100); |
| return; |
| } |
| |
| const themeIcon = themeToggle.querySelector('.theme-icon'); |
|
|
| |
| const currentTheme = localStorage.getItem('theme') || 'light'; |
| if (currentTheme === 'dark') { |
| body.classList.add('dark-theme'); |
| html.classList.add('dark-theme'); |
| document.documentElement.classList.add('dark-theme'); |
| if (themeIcon) themeIcon.textContent = '☀️'; |
| |
| setTimeout(() => { |
| applyInlineDarkThemeStyles(); |
| }, 0); |
| } else { |
| body.classList.remove('dark-theme'); |
| html.classList.remove('dark-theme'); |
| document.documentElement.classList.remove('dark-theme'); |
| if (themeIcon) themeIcon.textContent = '🌙'; |
| |
| removeInlineDarkThemeStyles(); |
| } |
|
|
| |
| if (!themeToggle.hasAttribute('data-listener-attached')) { |
| themeToggle.setAttribute('data-listener-attached', 'true'); |
| |
| |
| themeToggle.addEventListener('click', (e) => { |
| e.preventDefault(); |
| e.stopPropagation(); |
| |
| |
| const style = document.createElement('style'); |
| style.textContent = ` |
| *, *::before, *::after { |
| transition: none !important; |
| } |
| `; |
| document.head.appendChild(style); |
| |
| |
| const currentlyDark = body.classList.contains('dark-theme'); |
| if (currentlyDark) { |
| |
| body.classList.remove('dark-theme'); |
| html.classList.remove('dark-theme'); |
| document.documentElement.classList.remove('dark-theme'); |
| } else { |
| |
| body.classList.add('dark-theme'); |
| html.classList.add('dark-theme'); |
| document.documentElement.classList.add('dark-theme'); |
| } |
| const isDark = body.classList.contains('dark-theme'); |
| |
| |
| if (themeIcon) { |
| themeIcon.textContent = isDark ? '☀️' : '🌙'; |
| } |
| |
| |
| localStorage.setItem('theme', isDark ? 'dark' : 'light'); |
| |
| |
| if (isDark) { |
| applyInlineDarkThemeStyles(); |
| } else { |
| removeInlineDarkThemeStyles(); |
| } |
| |
| |
| requestAnimationFrame(() => { |
| requestAnimationFrame(() => { |
| if (document.head.contains(style)) { |
| document.head.removeChild(style); |
| } |
| }); |
| }); |
| }); |
| } |
| } |
|
|
| |
| function initializeThemeToggle() { |
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', initThemeToggle); |
| } else if (document.readyState === 'interactive' || document.readyState === 'complete') { |
| |
| initThemeToggle(); |
| } else { |
| |
| initThemeToggle(); |
| } |
| } |
|
|
| |
| initializeThemeToggle(); |
|
|
| |
| setTimeout(() => { |
| const themeToggle = document.getElementById('themeToggle'); |
| if (themeToggle && !themeToggle.hasAttribute('data-listener-attached')) { |
| initThemeToggle(); |
| } |
| }, 100); |
|
|
| |
| function initChatAgent() { |
| |
| const chatButton = document.getElementById('chatButton') || document.getElementById('chatAgentBtn'); |
| const chatWindow = document.getElementById('chatWindow'); |
| const chatClose = document.getElementById('chatClose'); |
| const chatInput = document.getElementById('chatInput'); |
| const chatSend = document.getElementById('chatSend'); |
| const chatMessages = document.getElementById('chatMessages'); |
|
|
| if (chatButton && chatWindow) { |
| |
| if (!chatButton.hasAttribute('data-chat-listener-attached')) { |
| chatButton.setAttribute('data-chat-listener-attached', 'true'); |
| chatButton.addEventListener('click', () => { |
| chatWindow.classList.toggle('active'); |
| }); |
| } |
| } |
| |
| if (chatClose) { |
| chatClose.addEventListener('click', () => { |
| chatWindow.classList.remove('active'); |
| }); |
| } |
| |
| if (chatSend && chatInput) { |
| const handleSend = async () => { |
| const message = chatInput.value.trim(); |
| if (!message) return; |
| |
| |
| if (message.startsWith('/setkey ')) { |
| const apiKey = message.substring(8).trim(); |
| localStorage.setItem('mistral_api_key', apiKey); |
| addChatMessage('✅ Mistral API key saved! You can now use AI-powered responses.', false); |
| chatInput.value = ''; |
| return; |
| } |
| |
| |
| addChatMessage(message, true); |
| chatInput.value = ''; |
| chatInput.disabled = true; |
| chatSend.disabled = true; |
| |
| |
| const typingId = addTypingIndicator(); |
| |
| try { |
| |
| const apiKey = localStorage.getItem('mistral_api_key'); |
| if (apiKey) { |
| const response = await getMistralResponse(message); |
| removeTypingIndicator(typingId); |
| addChatMessage(response, false); |
| } else { |
| |
| removeTypingIndicator(typingId); |
| const fallbackResponse = getFallbackResponse(message); |
| addChatMessage(fallbackResponse, false); |
| } |
| } catch (error) { |
| console.error('Chat error:', error); |
| removeTypingIndicator(typingId); |
| const fallbackResponse = getFallbackResponse(message); |
| addChatMessage(fallbackResponse, false); |
| } finally { |
| chatInput.disabled = false; |
| chatSend.disabled = false; |
| chatInput.focus(); |
| } |
| }; |
| |
| chatSend.addEventListener('click', handleSend); |
| chatInput.addEventListener('keypress', (e) => { |
| if (e.key === 'Enter' && !e.shiftKey) { |
| e.preventDefault(); |
| handleSend(); |
| } |
| }); |
| } |
| } |
|
|
| |
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', initChatAgent); |
| } else { |
| initChatAgent(); |
| } |
|
|
| |
| setTimeout(() => { |
| const chatButton = document.getElementById('chatButton') || document.getElementById('chatAgentBtn'); |
| if (chatButton && !chatButton.hasAttribute('data-chat-listener-attached')) { |
| chatButton.setAttribute('data-chat-listener-attached', 'true'); |
| initChatAgent(); |
| } |
| }, 100); |
|
|
| |
| let conversationHistory = []; |
|
|
| function addChatMessage(message, isUser = false) { |
| const chatMessages = document.getElementById('chatMessages'); |
| if (!chatMessages) return; |
| |
| const messageDiv = document.createElement('div'); |
| messageDiv.className = `chat-message ${isUser ? 'user' : 'bot'}-message`; |
| |
| const contentDiv = document.createElement('div'); |
| contentDiv.className = 'message-content'; |
| |
| |
| const formattedMessage = formatMessage(message); |
| contentDiv.innerHTML = formattedMessage; |
| |
| messageDiv.appendChild(contentDiv); |
| chatMessages.appendChild(messageDiv); |
| chatMessages.scrollTop = chatMessages.scrollHeight; |
| |
| |
| conversationHistory.push({ role: isUser ? 'user' : 'assistant', content: message }); |
| if (conversationHistory.length > 10) { |
| conversationHistory.shift(); |
| } |
| } |
|
|
| function formatMessage(text) { |
| |
| text = text.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1" target="_blank">$1</a>'); |
| |
| text = text.replace(/([a-z]+\/[^\.]+\.html)/g, '<a href="/$1">$1</a>'); |
| return text; |
| } |
|
|
| function addTypingIndicator() { |
| const chatMessages = document.getElementById('chatMessages'); |
| if (!chatMessages) return null; |
| |
| const typingDiv = document.createElement('div'); |
| typingDiv.className = 'chat-message bot-message typing-indicator'; |
| typingDiv.id = 'typing-indicator'; |
| typingDiv.innerHTML = '<div class="message-content"><span class="typing-dots">...</span></div>'; |
| chatMessages.appendChild(typingDiv); |
| chatMessages.scrollTop = chatMessages.scrollHeight; |
| return 'typing-indicator'; |
| } |
|
|
| function removeTypingIndicator(id) { |
| const indicator = document.getElementById(id); |
| if (indicator) { |
| indicator.remove(); |
| } |
| } |
|
|
| |
| async function getMistralResponse(userMessage) { |
| const apiKey = localStorage.getItem('mistral_api_key'); |
| if (!apiKey) { |
| throw new Error('No API key'); |
| } |
| |
| |
| if (!window.MISTRAL_TRAINING || !window.MISTRAL_TRAINING.KNOWLEDGE_BASE.length) { |
| if (window.MISTRAL_TRAINING && window.MISTRAL_TRAINING.loadKnowledgeBase) { |
| await window.MISTRAL_TRAINING.loadKnowledgeBase(); |
| } |
| } |
| |
| |
| const context = window.MISTRAL_TRAINING?.buildContext(userMessage) || ''; |
| |
| |
| const messages = [ |
| { |
| role: 'system', |
| content: (window.MISTRAL_TRAINING?.SYSTEM_PROMPT || '') + '\n\n' + context |
| }, |
| ...conversationHistory.slice(-8), |
| { role: 'user', content: userMessage } |
| ]; |
| |
| const response = await fetch('https://api.mistral.ai/v1/chat/completions', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| 'Authorization': `Bearer ${apiKey}` |
| }, |
| body: JSON.stringify({ |
| model: 'mistral-small', |
| messages: messages, |
| temperature: 0.7, |
| max_tokens: 1000 |
| }) |
| }); |
| |
| if (!response.ok) { |
| const error = await response.text(); |
| throw new Error(`Mistral API error: ${error}`); |
| } |
| |
| const data = await response.json(); |
| return data.choices[0]?.message?.content || 'I apologize, but I couldn\'t generate a response.'; |
| } |
|
|
| |
| function getFallbackResponse(message) { |
| const msg = message.toLowerCase(); |
| |
| |
| if (msg.includes('what is') || msg.includes('explain')) { |
| if (msg.includes('piandt')) { |
| return 'PIANDT is a Proportional Information Architecture for Networked Digital Transactions. It uses a triadic system (In, Processing, Out) to manage information flow. Would you like to know more about a specific stage?'; |
| } |
| if (msg.includes('triadic') || msg.includes('triad')) { |
| return 'The triadic information architecture has three stages: In (incoming signals), Processing (analysis and transformation), and Out (delivered outputs). Each stage maintains proportional relationships (S_In ∝ S_Proc ∝ S_Out).'; |
| } |
| } |
| |
| if (msg.includes('in ') || msg.includes('incoming')) { |
| return 'The In stage handles incoming signals from external stakeholders. You can explore In pages to see how different types of information are received.'; |
| } |
| |
| if (msg.includes('processing') || msg.includes('proc')) { |
| return 'The Processing stage analyzes and transforms incoming signals. Check the Processing pages to see how information is synthesized.'; |
| } |
| |
| if (msg.includes('out ') || msg.includes('output')) { |
| return 'The Out stage delivers processed signals to stakeholders. Visit Out pages to see how information is formatted and delivered.'; |
| } |
| |
| if (msg.includes('miu') || msg.includes('machine intelligence')) { |
| return 'MIU (Machine Intelligence Unit) is part of the Units section. It includes Vision, Products, and Services. Each has In, Processing, and Out variants.'; |
| } |
| |
| if (msg.includes('bidirectional') || msg.includes('both direction')) { |
| return 'Yes! PIANDT supports bidirectional information flow. Signals can flow from organization to external stakeholders and vice versa.'; |
| } |
| |
| if (msg.includes('automation') || msg.includes('automated')) { |
| return 'The structured nature of PIANDT enables full automation of business transactions through machine-readable formats and standardized protocols.'; |
| } |
| |
| |
| return 'I can help you navigate the PIANDT website! Ask me about:\n- The triadic information architecture (In, Processing, Out)\n- Specific pages or sections\n- MIU (Machine Intelligence Unit)\n- Bidirectionality and automation\n\nTo enable AI-powered responses, type: /setkey your-mistral-api-key'; |
| } |
|
|
| |
| |
| |
| |
| |
|
|
| (function() { |
| 'use strict'; |
| |
| |
| if (window.pageTransitionInitialized) return; |
| window.pageTransitionInitialized = true; |
| |
| |
| const FADE_IN_DURATION = 80; |
| const EASING = 'cubic-bezier(0.2, 0, 0.2, 1)'; |
| |
| |
| function initPageFadeIn() { |
| |
| document.body.style.opacity = '0'; |
| document.body.classList.add('page-fade-in'); |
| |
| |
| requestAnimationFrame(() => { |
| document.body.style.opacity = '1'; |
| |
| setTimeout(() => { |
| document.body.classList.remove('page-fade-in'); |
| }, FADE_IN_DURATION); |
| }); |
| } |
| |
| |
| function handleLinkClicks() { |
| |
| const links = document.querySelectorAll('a[href]'); |
| |
| links.forEach(link => { |
| |
| if (link.dataset.transitionHandler === 'true') return; |
| link.dataset.transitionHandler = 'true'; |
| |
| const href = link.getAttribute('href'); |
| |
| |
| if (!href || |
| href.startsWith('#') || |
| href.startsWith('javascript:') || |
| href.startsWith('mailto:') || |
| href.startsWith('tel:') || |
| (link.hasAttribute('target') && link.getAttribute('target') === '_blank') || |
| link.hasAttribute('download')) { |
| return; |
| } |
| |
| |
| try { |
| const url = new URL(href, window.location.origin); |
| if (url.origin !== window.location.origin) { |
| return; |
| } |
| } catch (e) { |
| |
| } |
| |
| |
| link.addEventListener('mouseenter', function() { |
| if (href && !href.startsWith('#')) { |
| |
| const linkElement = document.createElement('link'); |
| linkElement.rel = 'prefetch'; |
| linkElement.href = href; |
| document.head.appendChild(linkElement); |
| } |
| }, { passive: true }); |
| |
| |
| link.addEventListener('click', function(e) { |
| |
| if (e.ctrlKey || e.metaKey || e.shiftKey || e.button !== 0) { |
| return; |
| } |
| |
| |
| document.body.classList.add('page-transition-out'); |
| |
| |
| |
| }, { passive: true }); |
| }); |
| } |
| |
| |
| function handleBrowserNavigation() { |
| |
| window.addEventListener('pageshow', function(event) { |
| |
| document.body.classList.remove('page-transition-out'); |
| |
| |
| if (event.persisted) { |
| |
| document.body.style.opacity = '1'; |
| } else { |
| |
| initPageFadeIn(); |
| } |
| }); |
| |
| |
| window.addEventListener('beforeunload', function() { |
| |
| document.body.classList.add('page-transition-out'); |
| }); |
| } |
| |
| |
| function initPageTransitions() { |
| |
| document.body.classList.remove('page-transition-out'); |
| |
| |
| initPageFadeIn(); |
| |
| |
| handleLinkClicks(); |
| |
| |
| handleBrowserNavigation(); |
| } |
| |
| |
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', initPageTransitions); |
| } else { |
| |
| initPageTransitions(); |
| } |
| |
| |
| setTimeout(handleLinkClicks, 100); |
| |
| |
| const observer = new MutationObserver(function(mutations) { |
| let shouldReinit = false; |
| mutations.forEach(function(mutation) { |
| if (mutation.addedNodes.length > 0) { |
| mutation.addedNodes.forEach(function(node) { |
| if (node.nodeType === 1 && (node.tagName === 'A' || node.querySelector('a'))) { |
| shouldReinit = true; |
| } |
| }); |
| } |
| }); |
| if (shouldReinit) { |
| handleLinkClicks(); |
| } |
| }); |
| |
| |
| if (document.body) { |
| observer.observe(document.body, { |
| childList: true, |
| subtree: true |
| }); |
| } |
| |
| |
| document.addEventListener('mouseover', function(e) { |
| const link = e.target.closest('a[href]'); |
| if (link && link.dataset.transitionHandler === 'true') { |
| const href = link.getAttribute('href'); |
| if (href && !href.startsWith('#') && !href.startsWith('javascript:')) { |
| try { |
| const url = new URL(href, window.location.origin); |
| if (url.origin === window.location.origin) { |
| |
| const prefetchLink = document.createElement('link'); |
| prefetchLink.rel = 'prefetch'; |
| prefetchLink.href = href; |
| if (!document.querySelector(`link[rel="prefetch"][href="${href}"]`)) { |
| document.head.appendChild(prefetchLink); |
| } |
| } |
| } catch (e) { |
| |
| } |
| } |
| } |
| }, { passive: true }); |
| })(); |
|
|
| |
| |
| |
| |
|
|
| (function() { |
| 'use strict'; |
| |
| |
| if (window.resizeRefreshInitialized) return; |
| window.resizeRefreshInitialized = true; |
| |
| let resizeTimer; |
| let isRefreshing = false; |
| let lastWidth = window.innerWidth; |
| let lastHeight = window.innerHeight; |
| |
| |
| function getColumnCount() { |
| const width = window.innerWidth; |
| if (width < 768) return 1; |
| if (width < 1024) return 2; |
| if (width < 1440) return 3; |
| return 4; |
| } |
| |
| let lastColumnCount = getColumnCount(); |
| |
| |
| function hardRefresh() { |
| if (isRefreshing) return; |
| isRefreshing = true; |
| |
| |
| window.location.reload(true); |
| } |
| |
| |
| function handleResize() { |
| if (isRefreshing) return; |
| |
| const currentWidth = window.innerWidth; |
| const currentHeight = window.innerHeight; |
| const currentColumnCount = getColumnCount(); |
| |
| |
| if (currentColumnCount !== lastColumnCount) { |
| lastColumnCount = currentColumnCount; |
| clearTimeout(resizeTimer); |
| resizeTimer = setTimeout(hardRefresh, 200); |
| return; |
| } |
| |
| |
| const widthChanged = Math.abs(currentWidth - lastWidth) > 10; |
| const heightChanged = Math.abs(currentHeight - lastHeight) > 10; |
| |
| if (widthChanged || heightChanged) { |
| lastWidth = currentWidth; |
| lastHeight = currentHeight; |
| clearTimeout(resizeTimer); |
| resizeTimer = setTimeout(hardRefresh, 200); |
| } |
| } |
| |
| |
| function handleOrientationChange() { |
| if (isRefreshing) return; |
| setTimeout(hardRefresh, 100); |
| } |
| |
| |
| function observeColumnChanges() { |
| const contentElements = document.querySelectorAll('main, .content-grid, .section, .container'); |
| |
| if (contentElements.length > 0 && window.ResizeObserver) { |
| const resizeObserver = new ResizeObserver(function(entries) { |
| if (isRefreshing) return; |
| |
| for (let entry of entries) { |
| const currentColumnCount = getColumnCount(); |
| if (currentColumnCount !== lastColumnCount) { |
| lastColumnCount = currentColumnCount; |
| clearTimeout(resizeTimer); |
| resizeTimer = setTimeout(hardRefresh, 200); |
| break; |
| } |
| } |
| }); |
| |
| contentElements.forEach(function(element) { |
| resizeObserver.observe(element); |
| }); |
| } |
| } |
| |
| |
| function initResizeRefresh() { |
| |
| window.addEventListener('resize', handleResize, { passive: true }); |
| |
| |
| window.addEventListener('orientationchange', handleOrientationChange, { passive: true }); |
| |
| |
| observeColumnChanges(); |
| |
| |
| setTimeout(observeColumnChanges, 500); |
| } |
| |
| |
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', initResizeRefresh); |
| } else { |
| initResizeRefresh(); |
| } |
| |
| |
| if (document.body) { |
| const observer = new MutationObserver(function() { |
| observeColumnChanges(); |
| }); |
| |
| observer.observe(document.body, { |
| childList: true, |
| subtree: true |
| }); |
| } |
| })(); |
|
|
| |
| |
| |
| |
| |
| |
| |
| (function() { |
| const PIANDT_ADMIN_EMAIL = (typeof window !== 'undefined' && window.PIANDT_ADMIN_EMAIL) || ''; |
|
|
| function initCollaborateForm() { |
| const form = document.getElementById('collaborateForm'); |
| if (!form) return; |
|
|
| const statusEl = document.getElementById('collaborateFormStatus'); |
| const emailDisplay = document.getElementById('collabAdminEmailDisplay'); |
|
|
| if (emailDisplay) { |
| emailDisplay.textContent = PIANDT_ADMIN_EMAIL |
| ? PIANDT_ADMIN_EMAIL |
| : 'Admin email not set yet (placeholder)'; |
| } |
|
|
| form.addEventListener('submit', function(event) { |
| event.preventDefault(); |
|
|
| const name = (form.elements.namedItem('name') || {}).value || ''; |
| const organisation = (form.elements.namedItem('organisation') || {}).value || ''; |
| const email = (form.elements.namedItem('email') || {}).value || ''; |
| const scope = (form.elements.namedItem('scope') || {}).value || ''; |
| const message = (form.elements.namedItem('message') || {}).value || ''; |
|
|
| if (!name.trim() || !email.trim() || !scope || !message.trim()) { |
| if (statusEl) { |
| statusEl.className = 'form-status is-error'; |
| statusEl.textContent = 'Please complete the required fields.'; |
| } |
| return; |
| } |
|
|
| const payload = { |
| name: name.trim(), |
| organisation: organisation.trim(), |
| email: email.trim(), |
| scope: scope, |
| message: message.trim(), |
| submittedAt: new Date().toISOString() |
| }; |
|
|
| |
| try { |
| const key = 'piandt_collaboration_enquiries'; |
| const existing = JSON.parse(localStorage.getItem(key) || '[]'); |
| existing.push(payload); |
| localStorage.setItem(key, JSON.stringify(existing)); |
| } catch (err) { |
| console.warn('Could not store collaboration enquiry locally:', err); |
| } |
|
|
| if (!PIANDT_ADMIN_EMAIL) { |
| if (statusEl) { |
| statusEl.className = 'form-status is-pending'; |
| statusEl.textContent = 'Thank you. Your enquiry was saved locally. Email forwarding is a placeholder until an admin email is configured.'; |
| } |
| form.reset(); |
| return; |
| } |
|
|
| const subject = encodeURIComponent('PIANDT collaboration enquiry — ' + payload.name); |
| const body = encodeURIComponent( |
| [ |
| 'Name: ' + payload.name, |
| 'Organisation: ' + (payload.organisation || '(not provided)'), |
| 'Email: ' + payload.email, |
| 'Collaboration type: ' + payload.scope, |
| '', |
| payload.message, |
| '', |
| 'Submitted: ' + payload.submittedAt |
| ].join('\n') |
| ); |
| window.location.href = 'mailto:' + PIANDT_ADMIN_EMAIL + '?subject=' + subject + '&body=' + body; |
|
|
| if (statusEl) { |
| statusEl.className = 'form-status is-success'; |
| statusEl.textContent = 'Opening your email client to send this enquiry…'; |
| } |
| form.reset(); |
| }); |
| } |
|
|
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', initCollaborateForm); |
| } else { |
| initCollaborateForm(); |
| } |
| })(); |
|
|