LLM-test/test/code/1/deepseel-cc/js/events.js

598 lines
17 KiB
JavaScript

/* ==========================================================================
SYSTEM AWAKENING - Event Manager Module
Handles all user interactions and system events
========================================================================== */
import {
debounce,
throttle,
isTouchDevice
} from './config.js';
/**
* Event Manager Class
* Manages all event listeners and user interactions
*/
export class EventManager {
/**
* Create a new event manager
* @param {Object} components - System components that need event handling
*/
constructor(components = {}) {
// Component references
this.particleSystem = components.particleSystem;
this.progressCircles = components.progressCircles;
this.terminal = components.terminal;
this.typewriter = components.typewriter;
this.animationCoordinator = components.animationCoordinator;
// Event state
this.isResizing = false;
this.lastScrollY = 0;
this.scrollDirection = 'down';
this.mousePosition = { x: 0, y: 0 };
this.activeTouches = [];
this.isMobile = window.innerWidth <= 768;
// Throttled/debounced functions
this.handleResizeThrottled = throttle(this.handleResize.bind(this), 250);
this.handleScrollThrottled = throttle(this.handleScroll.bind(this), 100);
this.handleMouseMoveThrottled = throttle(this.handleMouseMove.bind(this), 50);
// Initialize
this.init();
}
/**
* Initialize the event manager
*/
init() {
// Bind all event listeners
this.bindEvents();
// Set up touch-specific optimizations
if (isTouchDevice()) {
this.setupTouchOptimizations();
}
// Initialize navigation
this.initNavigation();
console.log('Event manager initialized');
}
/**
* Bind all event listeners
*/
bindEvents() {
// Window events
window.addEventListener('resize', this.handleResizeThrottled);
window.addEventListener('scroll', this.handleScrollThrottled);
window.addEventListener('mousemove', this.handleMouseMoveThrottled);
// Mouse events for interactive effects
window.addEventListener('mousedown', this.handleMouseDown.bind(this));
window.addEventListener('mouseup', this.handleMouseUp.bind(this));
// Touch events for mobile
if (isTouchDevice()) {
this.bindTouchEvents();
}
// Keyboard events for shortcuts
document.addEventListener('keydown', this.handleKeyDown.bind(this));
// Page visibility for performance
document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
// Before unload for cleanup
window.addEventListener('beforeunload', this.handleBeforeUnload.bind(this));
}
/**
* Handle window resize
*/
handleResize() {
this.isMobile = window.innerWidth <= 768;
// Update particle system
if (this.particleSystem && this.particleSystem.onResize) {
this.particleSystem.onResize();
}
// Update progress circles
if (this.progressCircles && this.progressCircles.resize) {
this.progressCircles.resize();
}
// Dispatch custom event for other components
this.dispatchEvent('system:resize', {
width: window.innerWidth,
height: window.innerHeight,
isMobile: this.isMobile
});
// Log resize for debugging
console.log(`Window resized: ${window.innerWidth}x${window.innerHeight}`);
}
/**
* Handle window scroll
*/
handleScroll() {
const currentScrollY = window.scrollY;
// Determine scroll direction
this.scrollDirection = currentScrollY > this.lastScrollY ? 'down' : 'up';
this.lastScrollY = currentScrollY;
// Dispatch custom scroll event
this.dispatchEvent('system:scroll', {
scrollY: currentScrollY,
direction: this.scrollDirection
});
// Update navigation based on scroll position
this.updateNavigationOnScroll();
}
/**
* Handle mouse movement for interactive effects
* @param {MouseEvent} event - Mouse event
*/
handleMouseMove(event) {
this.mousePosition.x = event.clientX;
this.mousePosition.y = event.clientY;
// Optional: Add interactive particle effects
if (this.particleSystem && this.particleSystem.interactive) {
this.particleSystem.interactive.updateMousePosition(
this.mousePosition.x,
this.mousePosition.y
);
}
// Dispatch mouse move event
this.dispatchEvent('system:mouseMove', {
x: this.mousePosition.x,
y: this.mousePosition.y
});
}
/**
* Handle mouse down
*/
handleMouseDown() {
this.dispatchEvent('system:mouseDown');
}
/**
* Handle mouse up
*/
handleMouseUp() {
this.dispatchEvent('system:mouseUp');
}
/**
* Bind touch events for mobile devices
*/
bindTouchEvents() {
document.addEventListener('touchstart', this.handleTouchStart.bind(this));
document.addEventListener('touchmove', this.handleTouchMove.bind(this));
document.addEventListener('touchend', this.handleTouchEnd.bind(this));
document.addEventListener('touchcancel', this.handleTouchCancel.bind(this));
}
/**
* Handle touch start
* @param {TouchEvent} event - Touch event
*/
handleTouchStart(event) {
event.preventDefault();
this.activeTouches = Array.from(event.touches);
// Dispatch touch event
this.dispatchEvent('system:touchStart', {
touches: this.activeTouches
});
// Optional: Add touch-specific visual feedback
this.showTouchFeedback(event.touches[0]);
}
/**
* Handle touch move
* @param {TouchEvent} event - Touch event
*/
handleTouchMove(event) {
event.preventDefault();
this.activeTouches = Array.from(event.touches);
// Dispatch touch move event
this.dispatchEvent('system:touchMove', {
touches: this.activeTouches
});
}
/**
* Handle touch end
* @param {TouchEvent} event - Touch event
*/
handleTouchEnd(event) {
event.preventDefault();
this.activeTouches = Array.from(event.touches);
// Dispatch touch end event
this.dispatchEvent('system:touchEnd', {
touches: this.activeTouches
});
// Remove touch feedback
this.removeTouchFeedback();
}
/**
* Handle touch cancel
*/
handleTouchCancel() {
this.activeTouches = [];
// Dispatch touch cancel event
this.dispatchEvent('system:touchCancel');
// Remove touch feedback
this.removeTouchFeedback();
}
/**
* Show visual feedback for touch interactions
* @param {Touch} touch - Touch point
*/
showTouchFeedback(touch) {
// Create feedback element if it doesn't exist
if (!this.touchFeedback) {
this.touchFeedback = document.createElement('div');
this.touchFeedback.className = 'touch-feedback';
this.touchFeedback.style.cssText = `
position: fixed;
width: 60px;
height: 60px;
border-radius: 50%;
background: radial-gradient(circle, rgba(0,243,255,0.3) 0%, rgba(0,243,255,0) 70%);
pointer-events: none;
z-index: 1000;
transform: translate(-50%, -50%);
transition: opacity 0.3s ease;
`;
document.body.appendChild(this.touchFeedback);
}
// Update position
this.touchFeedback.style.left = `${touch.clientX}px`;
this.touchFeedback.style.top = `${touch.clientY}px`;
this.touchFeedback.style.opacity = '1';
}
/**
* Remove touch feedback
*/
removeTouchFeedback() {
if (this.touchFeedback) {
this.touchFeedback.style.opacity = '0';
}
}
/**
* Handle keyboard shortcuts
* @param {KeyboardEvent} event - Key event
*/
handleKeyDown(event) {
// Ctrl/Cmd + L: Clear terminal
if ((event.ctrlKey || event.metaKey) && event.key === 'l') {
event.preventDefault();
if (this.terminal && this.terminal.clear) {
this.terminal.clear();
}
}
// Escape: Pause/resume animations
if (event.key === 'Escape') {
if (this.animationCoordinator) {
if (this.animationCoordinator.isAnimationRunning()) {
this.animationCoordinator.pause();
} else {
this.animationCoordinator.resume();
}
}
}
// Space: Skip to next typewriter text
if (event.key === ' ') {
event.preventDefault();
if (this.typewriter && this.typewriter.next) {
this.typewriter.next();
}
}
}
/**
* Handle page visibility change
*/
handleVisibilityChange() {
const isVisible = document.visibilityState === 'visible';
if (isVisible) {
// Page became visible - resume animations
if (this.animationCoordinator && this.animationCoordinator.resume) {
this.animationCoordinator.resume();
}
} else {
// Page became hidden - pause animations to save resources
if (this.animationCoordinator && this.animationCoordinator.pause) {
this.animationCoordinator.pause();
}
}
this.dispatchEvent('system:visibilityChange', { isVisible });
}
/**
* Handle before unload for cleanup
*/
handleBeforeUnload() {
// Clean up resources
this.cleanup();
// Dispatch unload event
this.dispatchEvent('system:beforeUnload');
}
/**
* Set up optimizations for touch devices
*/
setupTouchOptimizations() {
// Increase tap target sizes
this.increaseTapTargets();
// Disable hover effects on touch devices
this.disableHoverEffects();
// Optimize scrolling performance
this.optimizeTouchScrolling();
}
/**
* Increase tap target sizes for better touch interaction
*/
increaseTapTargets() {
const style = document.createElement('style');
style.textContent = `
.nav-link,
.tab,
.control-btn {
min-height: 44px;
min-width: 44px;
}
/* Increase touch area around interactive elements */
.card {
padding: 1.5rem;
}
`;
document.head.appendChild(style);
}
/**
* Disable hover effects on touch devices
*/
disableHoverEffects() {
const style = document.createElement('style');
style.textContent = `
@media (hover: none) and (pointer: coarse) {
.card:hover {
transform: none !important;
}
.nav-link:hover {
background: transparent !important;
}
}
`;
document.head.appendChild(style);
}
/**
* Optimize scrolling performance for touch devices
*/
optimizeTouchScrolling() {
// Enable passive event listeners for better scrolling performance
const supportsPassive = (() => {
let supports = false;
try {
const opts = Object.defineProperty({}, 'passive', {
get: () => { supports = true; }
});
window.addEventListener('test', null, opts);
window.removeEventListener('test', null, opts);
} catch (e) {}
return supports;
})();
if (supportsPassive) {
// Re-bind scroll event with passive listener
window.removeEventListener('scroll', this.handleScrollThrottled);
window.addEventListener('scroll', this.handleScrollThrottled, {
passive: true
});
}
}
/**
* Initialize navigation event handlers
*/
initNavigation() {
// Set up smooth scrolling for anchor links
this.setupSmoothScrolling();
// Update active navigation on scroll
this.setupNavigationTracking();
}
/**
* Set up smooth scrolling for navigation links
*/
setupSmoothScrolling() {
const navLinks = document.querySelectorAll('a[href^="#"]');
navLinks.forEach(link => {
link.addEventListener('click', (event) => {
event.preventDefault();
const targetId = link.getAttribute('href');
const targetElement = document.querySelector(targetId);
if (targetElement) {
// Smooth scroll to target
targetElement.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// Update navigation state
this.updateNavigationState(targetId.substring(1));
}
});
});
}
/**
* Set up navigation tracking based on scroll position
*/
setupNavigationTracking() {
// This is called by the scroll handler
// Implementation in handleScroll and updateNavigationOnScroll
}
/**
* Update navigation state based on current section
* @param {string} sectionId - ID of the current section
*/
updateNavigationState(sectionId) {
// Update navigation links
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
const linkSectionId = link.getAttribute('href').substring(1);
if (linkSectionId === sectionId) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
});
// Dispatch navigation change event
this.dispatchEvent('system:navigationChange', { sectionId });
}
/**
* Update navigation based on scroll position
*/
updateNavigationOnScroll() {
// Get all sections
const sections = ['hero', 'manifesto', 'data-core', 'terminal'];
const scrollPosition = window.scrollY + 100; // Offset for early activation
// Find current section
for (const sectionId of sections) {
const section = document.getElementById(sectionId);
if (section) {
const sectionTop = section.offsetTop;
const sectionHeight = section.offsetHeight;
if (scrollPosition >= sectionTop &&
scrollPosition < sectionTop + sectionHeight) {
this.updateNavigationState(sectionId);
break;
}
}
}
}
/**
* Dispatch a custom event
* @param {string} eventName - Name of the event
* @param {Object} detail - Event details
*/
dispatchEvent(eventName, detail = {}) {
const event = new CustomEvent(eventName, {
detail: detail,
bubbles: true
});
document.dispatchEvent(event);
}
/**
* Clean up all event listeners and resources
*/
cleanup() {
// Remove event listeners
window.removeEventListener('resize', this.handleResizeThrottled);
window.removeEventListener('scroll', this.handleScrollThrottled);
window.removeEventListener('mousemove', this.handleMouseMoveThrottled);
document.removeEventListener('keydown', this.handleKeyDown);
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
// Remove touch event listeners
if (isTouchDevice()) {
document.removeEventListener('touchstart', this.handleTouchStart);
document.removeEventListener('touchmove', this.handleTouchMove);
document.removeEventListener('touchend', this.handleTouchEnd);
document.removeEventListener('touchcancel', this.handleTouchCancel);
}
// Remove touch feedback element
if (this.touchFeedback && this.touchFeedback.parentNode) {
this.touchFeedback.parentNode.removeChild(this.touchFeedback);
}
console.log('Event manager cleaned up');
}
/**
* Get current mouse position
* @returns {Object} Mouse coordinates {x, y}
*/
getMousePosition() {
return { ...this.mousePosition };
}
/**
* Get current scroll information
* @returns {Object} Scroll information {scrollY, direction}
*/
getScrollInfo() {
return {
scrollY: this.lastScrollY,
direction: this.scrollDirection
};
}
/**
* Check if device is mobile
* @returns {boolean} True if mobile
*/
isMobileDevice() {
return this.isMobile;
}
/**
* Clean up all resources
*/
dispose() {
this.cleanup();
console.log('Event manager disposed');
}
}