598 lines
17 KiB
JavaScript
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');
|
||
|
|
}
|
||
|
|
}
|