Harshasnade's picture
Initialize clean space deployment
ee00155
/**
* Mobile Navigation & Utilities
* Handles hamburger menu, touch events, and mobile-specific optimizations
*/
(function () {
'use strict';
// ==================== HAMBURGER MENU ====================
const hamburger = document.getElementById('hamburger');
const navMenuWrapper = document.querySelector('.nav-menu-wrapper');
const body = document.body;
if (hamburger && navMenuWrapper) {
// Toggle menu
hamburger.addEventListener('click', function () {
this.classList.toggle('active');
navMenuWrapper.classList.toggle('active');
body.classList.toggle('menu-open');
});
// Close menu when clicking on a nav link
const navLinks = navMenuWrapper.querySelectorAll('.nav-menu a, .btn-primary');
navLinks.forEach(link => {
link.addEventListener('click', function () {
hamburger.classList.remove('active');
navMenuWrapper.classList.remove('active');
body.classList.remove('menu-open');
});
});
// Close menu when clicking outside
document.addEventListener('click', function (event) {
const isClickInsideNav = navMenuWrapper.contains(event.target);
const isClickOnHamburger = hamburger.contains(event.target);
if (!isClickInsideNav && !isClickOnHamburger && navMenuWrapper.classList.contains('active')) {
hamburger.classList.remove('active');
navMenuWrapper.classList.remove('active');
body.classList.remove('menu-open');
}
});
// Close menu on ESC key
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape' && navMenuWrapper.classList.contains('active')) {
hamburger.classList.remove('active');
navMenuWrapper.classList.remove('active');
body.classList.remove('menu-open');
}
});
}
// ==================== VIEWPORT HEIGHT FIX (iOS) ====================
// Fix for 100vh on mobile browsers (address bar issue)
function setViewportHeight() {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
setViewportHeight();
window.addEventListener('resize', setViewportHeight);
window.addEventListener('orientationchange', setViewportHeight);
// ==================== TOUCH IMPROVEMENTS ====================
// Add touch-active class for better touch feedback
document.querySelectorAll('button, a, .tech-card, .showcase-item, .history-card').forEach(element => {
element.addEventListener('touchstart', function () {
this.classList.add('touch-active');
}, { passive: true });
element.addEventListener('touchend', function () {
this.classList.remove('touch-active');
}, { passive: true });
element.addEventListener('touchcancel', function () {
this.classList.remove('touch-active');
}, { passive: true });
});
// ==================== PREVENT ZOOM ON INPUT FOCUS ====================
// Already handled in CSS with font-size: 16px, but adding for completeness
const inputs = document.querySelectorAll('input, textarea, select');
inputs.forEach(input => {
input.addEventListener('focus', function () {
const viewport = document.querySelector('meta[name=viewport]');
if (viewport) {
viewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0';
}
});
input.addEventListener('blur', function () {
const viewport = document.querySelector('meta[name=viewport]');
if (viewport) {
viewport.content = 'width=device-width, initial-scale=1.0';
}
});
});
// ==================== MOBILE DETECTION ====================
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const isTablet = /(iPad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/.test(navigator.userAgent.toLowerCase());
if (isMobile) {
document.body.classList.add('is-mobile');
}
if (isTablet) {
document.body.classList.add('is-tablet');
}
// ==================== SMOOTH SCROLL POLYFILL ====================
// For browsers that don't support smooth scrolling
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
const target = document.querySelector(this.getAttribute('href'));
if (target) {
e.preventDefault();
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// ==================== DEBOUNCED RESIZE HANDLER ====================
let resizeTimer;
window.addEventListener('resize', function () {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
// Trigger custom event that other scripts can listen to
window.dispatchEvent(new CustomEvent('debouncedResize'));
}, 250);
});
// ==================== LAZY LOAD OPTIMIZATION ====================
// Only load images when they're about to enter the viewport
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
observer.unobserve(img);
}
}
});
}, {
rootMargin: '50px'
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
}
// ==================== PREVENT OVERSCROLL (iOS) ====================
// Prevent rubber-band scrolling on iOS
let scrollStartY = 0;
document.addEventListener('touchstart', function (e) {
scrollStartY = e.touches[0].pageY;
}, { passive: true });
document.addEventListener('touchmove', function (e) {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = document.documentElement.clientHeight;
const scrollY = e.touches[0].pageY;
// Prevent overscroll at top
if (scrollTop === 0 && scrollY > scrollStartY) {
e.preventDefault();
}
// Prevent overscroll at bottom
if (scrollTop + clientHeight >= scrollHeight && scrollY < scrollStartY) {
e.preventDefault();
}
}, { passive: false });
// ==================== PERFORMANCE OPTIMIZATION ====================
// Reduce animations on low-end devices
if (navigator.hardwareConcurrency && navigator.hardwareConcurrency < 4) {
document.body.classList.add('reduce-motion');
}
// Detect slow connection
if ('connection' in navigator) {
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (connection && (connection.effectiveType === '2g' || connection.effectiveType === 'slow-2g')) {
document.body.classList.add('slow-connection');
// Disable heavy animations
document.querySelectorAll('.floating-3d-object').forEach(el => {
el.style.display = 'none';
});
}
}
// ==================== HORIZONTAL SCROLL INDICATOR ====================
// Add scroll indicator for tables on mobile
const scrollableElements = document.querySelectorAll('.history-table-container, .pipeline');
scrollableElements.forEach(element => {
if (element.scrollWidth > element.clientWidth) {
element.classList.add('has-horizontal-scroll');
// Remove indicator after first scroll
element.addEventListener('scroll', function () {
this.classList.remove('has-horizontal-scroll');
}, { once: true });
}
});
// ==================== STATUS BAR COLOR (PWA) ====================
// Set theme color for mobile browsers
const metaThemeColor = document.querySelector('meta[name=theme-color]');
if (!metaThemeColor) {
const meta = document.createElement('meta');
meta.name = 'theme-color';
meta.content = '#000000';
document.head.appendChild(meta);
}
console.log('🚀 Mobile optimizations loaded');
})();