PIANDT / PIANDT__backup /script.js
0001AMA's picture
Add PIANDT__backup folder (binaries excluded for HF)
90e6c1c
Raw
History Blame Contribute Delete
45.6 kB
// Mobile Navigation Toggle
const hamburger = document.querySelector('.hamburger');
const navMenu = document.querySelector('.nav-menu');
if (hamburger) {
hamburger.addEventListener('click', () => {
navMenu.classList.toggle('active');
hamburger.classList.toggle('active');
});
}
// Auto-position submenus consistently per parent menu level
// All child menus within a parent (In/Proc/Out) point in the same direction
// If ANY child menu level would equal or exceed the right margin, ALL menus point leftward
//
// FUTURE-PROOF: This function automatically works for new pages/menus because:
// - Uses generic selectors (.nav-menu > .dropdown) - finds any top-level parent
// - Dynamically detects parent type from link text/href - no hardcoded names
// - Finds ALL child menus at ALL nesting levels - works for any depth
// - Uses MutationObserver to detect DOM changes - catches dynamically added menus
// - Re-runs on window resize - adapts to viewport changes
function initSubmenuPositioning() {
// Find all top-level dropdown menus (In, Proc, Out, or any future parents)
// This selector is generic and will find any top-level dropdown menu
const topLevelDropdowns = document.querySelectorAll('.nav-menu > .dropdown');
topLevelDropdowns.forEach(parentDropdown => {
const parentLink = parentDropdown.querySelector('a.nav-link');
if (!parentLink) return;
// Determine which parent this is (In, Proc, or Out)
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';
}
// Get the parent dropdown menu
const parentDropdownMenu = parentDropdown.querySelector('.dropdown-menu');
if (!parentDropdownMenu) return;
// Calculate if this parent menu should point leftward or rightward
function determineParentDirection() {
const parentRect = parentDropdown.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const padding = 10;
const safetyMargin = 20;
const rightMargin = viewportWidth - padding - safetyMargin;
// Find maximum nesting depth
let maxDepth = 0;
function findMaxDepth(element, currentDepth = 0) {
// Get direct child li elements, then their direct child ul elements
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);
// Estimate submenu width (average width for nested menus)
const estimatedSubmenuWidth = 220;
// Calculate rightmost edge position if all menus point rightward
// This represents the worst-case scenario
const rightmostEdgePosition = parentRect.right + (estimatedSubmenuWidth * maxDepth);
// Debug logging
console.log(`[Menu Direction] ${parentType || 'unknown'}: parent.right=${parentRect.right.toFixed(0)}, maxDepth=${maxDepth}, rightmostEdge=${rightmostEdgePosition.toFixed(0)}, rightMargin=${rightMargin.toFixed(0)}`);
// Check if rightmost edge would equal or exceed right margin
if (rightmostEdgePosition >= rightMargin) {
// ANY menu level would exceed right margin → point ALL leftward
console.log(`[Menu Direction] ${parentType || 'unknown'}: OVERFLOW DETECTED → pointing LEFTWARD`);
return 'leftward';
}
// Also check each menu level individually
const allChildMenus = parentDropdownMenu.querySelectorAll('ul');
for (let childMenu of allChildMenus) {
// Calculate depth of this menu
let depth = 0;
let current = childMenu;
while (current && current !== parentDropdownMenu) {
if (current.tagName === 'UL') {
depth++;
}
current = current.parentElement;
}
// Get parent menu item
const parentMenuItem = childMenu.parentElement;
if (parentMenuItem) {
const menuItemRect = parentMenuItem.getBoundingClientRect();
// Calculate where this menu's right edge would be
const estimatedRightEdge = menuItemRect.right + estimatedSubmenuWidth;
// If this menu would equal or exceed right margin, point all leftward
if (estimatedRightEdge >= rightMargin) {
console.log(`[Menu Direction] ${parentType || 'unknown'}: Individual menu overflow → pointing LEFTWARD`);
return 'leftward';
}
}
}
// For Out and Proc, also check if parent is close to right edge
if (parentType === 'out' || parentType === 'processing') {
const spaceOnRight = viewportWidth - parentRect.right - padding - safetyMargin;
const spaceOnLeft = parentRect.left - padding;
// If parent is in right 70% of screen or space on right is limited, prefer leftward
if (parentRect.right > viewportWidth * 0.7 || spaceOnRight < spaceOnLeft * 1.2) {
console.log(`[Menu Direction] ${parentType}: Close to right edge → pointing LEFTWARD`);
return 'leftward';
}
}
// Default: rightward if there's enough space
console.log(`[Menu Direction] ${parentType || 'unknown'}: Enough space → pointing RIGHTWARD`);
return 'rightward';
}
// Determine direction for this parent menu
const parentDirection = determineParentDirection();
// Find ALL child menus at ALL levels within this parent
const allChildMenus = parentDropdownMenu.querySelectorAll('ul');
// Apply consistent direction to all child menus with STRONG inline styles
allChildMenus.forEach(childMenu => {
if (parentDirection === 'leftward') {
// Point leftward - add class and set styles
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 {
// Point rightward - remove class and set styles
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');
}
});
// Set up hover handlers to maintain consistency
const menuItemsWithChildren = parentDropdownMenu.querySelectorAll('li:has(> ul)');
menuItemsWithChildren.forEach(menuItem => {
// Get direct child ul (not nested)
const submenu = Array.from(menuItem.children).find(child => child.tagName === 'UL');
if (!submenu) return;
// On hover, ensure consistent direction is maintained
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');
}
});
});
});
// Re-check on window resize
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
initSubmenuPositioning();
}, 100);
});
}
// Initialize submenu positioning when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSubmenuPositioning);
} else {
initSubmenuPositioning();
}
// Re-initialize after dynamic menus are loaded
setTimeout(initSubmenuPositioning, 600);
setTimeout(initSubmenuPositioning, 1000);
// Also observe DOM changes for dynamically added menus
// FUTURE-PROOF: This ensures new menus added via JavaScript are automatically positioned
const observer = new MutationObserver((mutations) => {
// Only re-run if dropdown menus were added/modified
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 });
// Close mobile menu when clicking on a link
const navLinks = document.querySelectorAll('.nav-link, .dropdown-link, .submenu-link');
navLinks.forEach(link => {
link.addEventListener('click', () => {
navMenu.classList.remove('active');
hamburger.classList.remove('active');
});
});
// Handle dropdown menus on mobile
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');
}
});
}
});
// Theme Toggle Functionality - Helper functions
// Inject stylesheet immediately if dark theme is detected (runs before DOMContentLoaded)
(function() {
if (typeof localStorage !== 'undefined') {
const savedTheme = localStorage.getItem('theme') || 'light';
if (savedTheme === 'dark') {
// Inject stylesheet immediately in head
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;
}
`;
// Insert immediately if head exists, otherwise wait for it
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() {
// Inject a dynamic stylesheet to override everything - most aggressive approach
let darkThemeStyle = document.getElementById('dark-theme-override-style');
if (!darkThemeStyle) {
darkThemeStyle = document.createElement('style');
darkThemeStyle.id = 'dark-theme-override-style';
// Insert at the end of head to ensure it overrides everything
document.head.appendChild(darkThemeStyle);
}
// Use a comprehensive stylesheet that targets every possible selector
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;
}
`;
// Also force update CSS variables on html and body first
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');
// Batch DOM updates for better performance
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');
// Update main containers first with CSS variables
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');
}
// Update all content-text elements and their children in one pass
contentTextElements.forEach(el => {
// Set CSS variables on the element itself
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');
// CRITICAL: Update paragraphs FIRST with direct color override
const paragraphs = el.querySelectorAll('p');
paragraphs.forEach(p => {
// Force color directly - this overrides CSS variable usage
p.style.setProperty('color', '#ffffff', 'important');
p.style.setProperty('--color-text-light', '#ffffff', 'important');
});
// Update ALL other children recursively
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');
});
});
// Also update h1 in content-grid
const h1Elements = document.querySelectorAll('.content-grid h1, .content-text h1');
h1Elements.forEach(h1 => {
h1.style.setProperty('color', '#ffffff', 'important');
});
}
function removeInlineDarkThemeStyles() {
// Remove the dynamic stylesheet
const darkThemeStyle = document.getElementById('dark-theme-override-style');
if (darkThemeStyle) {
darkThemeStyle.remove();
}
// CRITICAL: Remove dark background from html and body
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');
// Remove CSS variables from html and body
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');
// Batch DOM updates for better performance
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');
// Update main containers first - explicitly set to white background
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');
}
// Remove styles from all content-text elements and their children in one pass
contentTextElements.forEach(el => {
el.style.removeProperty('--color-text');
el.style.removeProperty('--color-text-light');
el.style.removeProperty('color');
el.style.removeProperty('background-color');
// Remove from paragraphs first
const paragraphs = el.querySelectorAll('p');
paragraphs.forEach(p => {
p.style.removeProperty('color');
p.style.removeProperty('--color-text-light');
});
// Remove from ALL other children
const allChildren = el.querySelectorAll('*:not(p)');
allChildren.forEach(child => {
child.style.removeProperty('color');
});
});
// Remove from h1 elements
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) {
// Retry if element not found yet
setTimeout(initThemeToggle, 100);
return;
}
const themeIcon = themeToggle.querySelector('.theme-icon');
// Check for saved theme preference or default to light mode
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 = '☀️';
// Apply inline styles on load if dark theme - use setTimeout to ensure DOM is ready
setTimeout(() => {
applyInlineDarkThemeStyles();
}, 0);
} else {
body.classList.remove('dark-theme');
html.classList.remove('dark-theme');
document.documentElement.classList.remove('dark-theme');
if (themeIcon) themeIcon.textContent = '🌙';
// Ensure no inline styles if light theme
removeInlineDarkThemeStyles();
}
// Mark that listener is attached
if (!themeToggle.hasAttribute('data-listener-attached')) {
themeToggle.setAttribute('data-listener-attached', 'true');
// Toggle theme - instant seamless change
themeToggle.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
// Disable transitions temporarily for instant theme change
const style = document.createElement('style');
style.textContent = `
*, *::before, *::after {
transition: none !important;
}
`;
document.head.appendChild(style);
// Immediately toggle classes on all elements - ensure class is properly set
const currentlyDark = body.classList.contains('dark-theme');
if (currentlyDark) {
// Switching to light
body.classList.remove('dark-theme');
html.classList.remove('dark-theme');
document.documentElement.classList.remove('dark-theme');
} else {
// Switching to dark
body.classList.add('dark-theme');
html.classList.add('dark-theme');
document.documentElement.classList.add('dark-theme');
}
const isDark = body.classList.contains('dark-theme');
// Update icon immediately
if (themeIcon) {
themeIcon.textContent = isDark ? '☀️' : '🌙';
}
// Save preference (non-blocking)
localStorage.setItem('theme', isDark ? 'dark' : 'light');
// Apply or remove inline styles based on theme - do it immediately
if (isDark) {
applyInlineDarkThemeStyles();
} else {
removeInlineDarkThemeStyles();
}
// Re-enable transitions after a brief moment (for other interactions)
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (document.head.contains(style)) {
document.head.removeChild(style);
}
});
});
});
}
}
// Initialize theme toggle when DOM is ready - multiple fallbacks
function initializeThemeToggle() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initThemeToggle);
} else if (document.readyState === 'interactive' || document.readyState === 'complete') {
// DOM already loaded or nearly loaded
initThemeToggle();
} else {
// Fallback: try immediately and retry if needed
initThemeToggle();
}
}
// Try multiple initialization methods
initializeThemeToggle();
// Also try after a short delay as fallback
setTimeout(() => {
const themeToggle = document.getElementById('themeToggle');
if (themeToggle && !themeToggle.hasAttribute('data-listener-attached')) {
initThemeToggle();
}
}, 100);
// Chat Agent Functionality
function initChatAgent() {
// Support both chatButton and chatAgentBtn IDs
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) {
// Mark listener as attached to prevent duplicates
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) {
chatSend.addEventListener('click', () => {
const message = chatInput.value.trim();
if (message) {
addChatMessage(message, true);
chatInput.value = '';
// Simulate bot response
setTimeout(() => {
addChatMessage('Thank you for your message. How can I help you?');
}, 500);
}
});
chatInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
chatSend.click();
}
});
}
}
// Initialize chat agent when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initChatAgent);
} else {
initChatAgent();
}
// Also try after a short delay as fallback
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);
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'}`;
messageDiv.textContent = message;
chatMessages.appendChild(messageDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// ============================================
// Movie-Like Seamless Page Transitions
// ============================================
// Provides invisible, ultra-smooth transitions like a movie
// Crossfade technique for seamless navigation
(function() {
'use strict';
// Prevent multiple initializations
if (window.pageTransitionInitialized) return;
window.pageTransitionInitialized = true;
// Configuration for movie-like transitions - no blank screen
const FADE_IN_DURATION = 80; // milliseconds - very fast
const EASING = 'cubic-bezier(0.2, 0, 0.2, 1)'; // Linear-like for seamless feel
// Initialize page - subtle fade-in only
function initPageFadeIn() {
// Start with opacity 0 for subtle fade-in
document.body.style.opacity = '0';
document.body.classList.add('page-fade-in');
// Fade in immediately
requestAnimationFrame(() => {
document.body.style.opacity = '1';
// Remove fade-in class after animation
setTimeout(() => {
document.body.classList.remove('page-fade-in');
}, FADE_IN_DURATION);
});
}
// Handle link clicks with movie-like crossfade
function handleLinkClicks() {
// Get all internal links (same origin)
const links = document.querySelectorAll('a[href]');
links.forEach(link => {
// Skip if already has transition handler
if (link.dataset.transitionHandler === 'true') return;
link.dataset.transitionHandler = 'true';
const href = link.getAttribute('href');
// Skip if it's not a same-origin link or special links
if (!href ||
href.startsWith('#') ||
href.startsWith('javascript:') ||
href.startsWith('mailto:') ||
href.startsWith('tel:') ||
(link.hasAttribute('target') && link.getAttribute('target') === '_blank') ||
link.hasAttribute('download')) {
return;
}
// Check if it's a same-origin link
try {
const url = new URL(href, window.location.origin);
if (url.origin !== window.location.origin) {
return; // External link, skip
}
} catch (e) {
// Relative URL, proceed
}
// Preload page on hover for instant transition
link.addEventListener('mouseenter', function() {
if (href && !href.startsWith('#')) {
// Prefetch the page for instant loading
const linkElement = document.createElement('link');
linkElement.rel = 'prefetch';
linkElement.href = href;
document.head.appendChild(linkElement);
}
}, { passive: true });
// Add click handler - no fade-out to prevent blank screen
link.addEventListener('click', function(e) {
// Don't interfere with special clicks (Ctrl/Cmd, middle mouse, etc.)
if (e.ctrlKey || e.metaKey || e.shiftKey || e.button !== 0) {
return;
}
// Disable pointer events but keep page visible - no blank screen
document.body.classList.add('page-transition-out');
// Allow navigation to proceed - browser handles the rest
// The new page will fade in seamlessly without blank screen
}, { passive: true });
});
}
// Handle browser back/forward navigation
function handleBrowserNavigation() {
// Use pageshow event for seamless back/forward
window.addEventListener('pageshow', function(event) {
// Remove transition-out class
document.body.classList.remove('page-transition-out');
// If page was loaded from cache (back/forward)
if (event.persisted) {
// Instant appearance - no fade needed
document.body.style.opacity = '1';
} else {
// Regular page load - subtle fade-in
initPageFadeIn();
}
});
// Handle page unload - no fade-out to prevent blank screen
window.addEventListener('beforeunload', function() {
// Just disable pointer events, keep page visible
document.body.classList.add('page-transition-out');
});
}
// Initialize when DOM is ready
function initPageTransitions() {
// Remove any existing transition classes
document.body.classList.remove('page-transition-out');
// Initialize instant appearance
initPageFadeIn();
// Setup link handlers
handleLinkClicks();
// Setup browser navigation handlers
handleBrowserNavigation();
}
// Initialize based on document state
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initPageTransitions);
} else {
// DOM already loaded
initPageTransitions();
}
// Also initialize after a short delay to catch dynamically added links
setTimeout(handleLinkClicks, 100);
// Re-initialize link handlers when new content is added (for dynamic menus)
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();
}
});
// Observe the document for new links
if (document.body) {
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// Preload pages on menu hover for instant transitions
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) {
// Prefetch for instant loading
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) {
// Invalid URL, skip
}
}
}
}, { passive: true });
})();
// ============================================
// Hard Refresh on Resize and Column Changes
// ============================================
// Automatically hard refreshes page when viewport or column sizes change
(function() {
'use strict';
// Prevent multiple initializations
if (window.resizeRefreshInitialized) return;
window.resizeRefreshInitialized = true;
let resizeTimer;
let isRefreshing = false;
let lastWidth = window.innerWidth;
let lastHeight = window.innerHeight;
// Get current column count based on viewport width - MUST be defined first
function getColumnCount() {
const width = window.innerWidth;
if (width < 768) return 1; // Mobile
if (width < 1024) return 2; // Tablet
if (width < 1440) return 3; // Desktop
return 4; // Large desktop
}
let lastColumnCount = getColumnCount();
// Hard refresh the page - simple and direct
function hardRefresh() {
if (isRefreshing) return;
isRefreshing = true;
// Force hard refresh - bypass cache
window.location.reload(true);
}
// Handle resize events - refresh on ANY resize
function handleResize() {
if (isRefreshing) return;
const currentWidth = window.innerWidth;
const currentHeight = window.innerHeight;
const currentColumnCount = getColumnCount();
// Check if column count changed (breakpoint crossed)
if (currentColumnCount !== lastColumnCount) {
lastColumnCount = currentColumnCount;
clearTimeout(resizeTimer);
resizeTimer = setTimeout(hardRefresh, 200);
return;
}
// Check if size changed significantly
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);
}
}
// Handle orientation changes
function handleOrientationChange() {
if (isRefreshing) return;
setTimeout(hardRefresh, 100);
}
// Monitor column changes with ResizeObserver
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);
});
}
}
// Initialize immediately
function initResizeRefresh() {
// Listen for window resize
window.addEventListener('resize', handleResize, { passive: true });
// Listen for orientation changes
window.addEventListener('orientationchange', handleOrientationChange, { passive: true });
// Monitor column changes
observeColumnChanges();
// Re-observe after delay for dynamic content
setTimeout(observeColumnChanges, 500);
}
// Initialize immediately
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initResizeRefresh);
} else {
initResizeRefresh();
}
// Re-observe when new content is added
if (document.body) {
const observer = new MutationObserver(function() {
observeColumnChanges();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
})();