1/** 2 * ð±ð» WikiLive Device Detection Utility 3 * Comprehensive device, platform, and capability detection for responsive UI adaptation 4 */ 5 6class DeviceDetector { 7 constructor() { 8 this.userAgent = navigator.userAgent.toLowerCase(); 9 this.platform = navigator.platform.toLowerCase(); 10 this.deviceInfo = this.detectDevice(); 11 this.capabilities = this.detectCapabilities(); 12 13 // Initialize device-specific CSS classes 14 this.applyDeviceClasses(); 15 16 // Log device detection results for debugging 17 console.log('ð Device Detection Results:', { 18 device: this.deviceInfo, 19 capabilities: this.capabilities, 20 viewport: this.getViewportInfo() 21 }); 22 } 23 24 /** 25 * ð± Detect device type and platform 26 */ 27 detectDevice() { 28 const device = { 29 type: 'desktop', // default 30 platform: 'unknown', 31 os: 'unknown', 32 browser: 'unknown', 33 isMobile: false, 34 isTablet: false, 35 isDesktop: true 36 }; 37 38 // Mobile device detection 39 const mobileRegex = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i; 40 const tabletRegex = /ipad|android(?!.*mobile)|tablet/i; 41 42 if (mobileRegex.test(this.userAgent)) { 43 device.isMobile = true; 44 device.isDesktop = false; 45 device.type = 'mobile'; 46 } 47 48 if (tabletRegex.test(this.userAgent)) { 49 device.isTablet = true; 50 device.isMobile = false; 51 device.isDesktop = false; 52 device.type = 'tablet'; 53 } 54 55 // Platform detection 56 if (/iphone|ipad|ipod/i.test(this.userAgent)) { 57 device.platform = 'ios'; 58 device.os = 'iOS'; 59 } else if (/android/i.test(this.userAgent)) { 60 device.platform = 'android'; 61 device.os = 'Android'; 62 } else if (/mac/i.test(this.platform)) { 63 device.platform = 'mac'; 64 device.os = 'macOS'; 65 } else if (/win/i.test(this.platform)) { 66 device.platform = 'windows'; 67 device.os = 'Windows'; 68 } else if (/linux/i.test(this.platform)) { 69 device.platform = 'linux'; 70 device.os = 'Linux'; 71 } 72 73 // Browser detection 74 if (/chrome/i.test(this.userAgent) && !/edge/i.test(this.userAgent)) { 75 device.browser = 'chrome'; 76 } else if (/firefox/i.test(this.userAgent)) { 77 device.browser = 'firefox'; 78 } else if (/safari/i.test(this.userAgent) && !/chrome/i.test(this.userAgent)) { 79 device.browser = 'safari'; 80 } else if (/edge/i.test(this.userAgent)) { 81 device.browser = 'edge'; 82 } 83 84 return device; 85 } 86 87 /** 88 * ð¯ Detect device capabilities 89 */ 90 detectCapabilities() { 91 return { 92 touchSupport: 'ontouchstart' in window || navigator.maxTouchPoints > 0, 93 fullscreenSupport: !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled), 94 orientationSupport: 'orientation' in window, 95 vibrationSupport: 'vibrate' in navigator, 96 geolocationSupport: 'geolocation' in navigator, 97 notificationSupport: 'Notification' in window, 98 serviceWorkerSupport: 'serviceWorker' in navigator, 99 webRTCSupport: !!(window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection) 100 }; 101 } 102 103 /** 104 * ð Get current viewport information 105 */ 106 getViewportInfo() { 107 return { 108 width: window.innerWidth, 109 height: window.innerHeight, 110 ratio: window.devicePixelRatio || 1, 111 orientation: window.innerWidth > window.innerHeight ? 'landscape' : 'portrait' 112 }; 113 } 114 115 /** 116 * ð± Get responsive breakpoint 117 */ 118 getBreakpoint() { 119 const width = window.innerWidth; 120 121 if (width < 480) return 'xs'; // Extra small (mobile portrait) 122 if (width < 768) return 'sm'; // Small (mobile landscape) 123 if (width < 1024) return 'md'; // Medium (tablet) 124 if (width < 1440) return 'lg'; // Large (desktop) 125 return 'xl'; // Extra large (wide desktop) 126 } 127 128 /** 129 * ð¨ Apply device-specific CSS classes to body 130 */ 131 applyDeviceClasses() { 132 const body = document.body; 133 const classes = []; 134 135 // Device type classes 136 classes.push(`device-${this.deviceInfo.type}`); 137 classes.push(`platform-${this.deviceInfo.platform}`); 138 classes.push(`os-${this.deviceInfo.os.toLowerCase().replace(/\s+/g, '-')}`); 139 classes.push(`browser-${this.deviceInfo.browser}`); 140 141 // Capability classes 142 if (this.capabilities.touchSupport) classes.push('touch-enabled'); 143 if (this.capabilities.fullscreenSupport) classes.push('fullscreen-capable'); 144 if (this.deviceInfo.isMobile) classes.push('is-mobile'); 145 if (this.deviceInfo.isTablet) classes.push('is-tablet'); 146 if (this.deviceInfo.isDesktop) classes.push('is-desktop'); 147
148 // Breakpoint class 149 classes.push(`breakpoint-${this.getBreakpoint()}`); 150 151 // Apply all classes 152 body.classList.add(...classes); 153 154 // Set CSS custom properties for JavaScript access 155 document.documentElement.style.setProperty('--device-type', this.deviceInfo.type); 156 document.documentElement.style.setProperty('--viewport-width', `${window.innerWidth}px`); 157 document.documentElement.style.setProperty('--viewport-height', `${window.innerHeight}px`); 158 } 159 160 /** 161 * ð Update classes on resize/orientation change 162 */ 163 updateOnResize() { 164 // Remove old breakpoint class 165 const body = document.body; 166 body.classList.remove('breakpoint-xs', 'breakpoint-sm', 'breakpoint-md', 'breakpoint-lg', 'breakpoint-xl'); 167 168 // Add new breakpoint class 169 body.classList.add(`breakpoint-${this.getBreakpoint()}`); 170 171 // Update CSS custom properties 172 document.documentElement.style.setProperty('--viewport-width', `${window.innerWidth}px`); 173 document.documentElement.style.setProperty('--viewport-height', `${window.innerHeight}px`); 174 175 // Update orientation 176 const viewport = this.getViewportInfo(); 177 document.documentElement.style.setProperty('--orientation', viewport.orientation); 178 } 179 180 /** 181 * ð¯ Check if device matches criteria 182 */ 183 is(criteria) { 184 if (typeof criteria === 'string') { 185 return this.deviceInfo.type === criteria || 186 this.deviceInfo.platform === criteria || 187 this.deviceInfo.os.toLowerCase() === criteria.toLowerCase(); 188 } 189 190 if (typeof criteria === 'object') { 191 return Object.keys(criteria).every(key => 192 this.deviceInfo[key] === criteria[key] || 193 this.capabilities[key] === criteria[key] 194 ); 195 } 196 197 return false; 198 } 199 200 /** 201 * ð± Convenience methods 202 */ 203 isMobile() { return this.deviceInfo.isMobile; } 204 isTablet() { return this.deviceInfo.isTablet; } 205 isDesktop() { return this.deviceInfo.isDesktop; } 206 isIOS() { return this.deviceInfo.platform === 'ios'; } 207 isAndroid() { return this.deviceInfo.platform === 'android'; } 208 isMac() { return this.deviceInfo.platform === 'mac'; } 209 isWindows() { return this.deviceInfo.platform === 'windows'; } 210 hasTouch() { return this.capabilities.touchSupport; } 211 canFullscreen() { return this.capabilities.fullscreenSupport; } 212 213 /** 214 * ð¨ Get device-specific styling recommendations 215 */ 216 getStyleRecommendations() { 217 const recommendations = { 218 buttonSize: this.isMobile() ? 'large' : 'medium', 219 fontSize: this.isMobile() ? '16px' : '14px', 220 spacing: this.isMobile() ? 'loose' : 'normal', 221 animations: this.isMobile() ? 'reduced' : 'full', 222 layout: this.isDesktop() ? 'multi-column' : 'single-column' 223 }; 224 225 return recommendations; 226 } 227} 228 229// ð Initialize device detector when DOM is ready 230let deviceDetector; 231 232function initializeDeviceDetection() { 233 deviceDetector = new DeviceDetector(); 234 235 // Listen for resize and orientation changes 236 let resizeTimeout; 237 window.addEventListener('resize', () => { 238 clearTimeout(resizeTimeout); 239 resizeTimeout = setTimeout(() => { 240 deviceDetector.updateOnResize(); 241 console.log('ð Viewport updated:', deviceDetector.getViewportInfo()); 242 }, 100); 243 }); 244 245 // Listen for orientation changes 246 window.addEventListener('orientationchange', () => { 247 setTimeout(() => { 248 deviceDetector.updateOnResize(); 249 console.log('ð Orientation changed:', deviceDetector.getViewportInfo()); 250 }, 100); 251 }); 252 253 // Make device detector globally available 254 window.deviceDetector = deviceDetector; 255 256 console.log('â Device detection initialized successfully'); 257} 258 259// Auto-initialize when DOM is ready 260if (document.readyState === 'loading') { 261 document.addEventListener('DOMContentLoaded', initializeDeviceDetection); 262} else { 263 initializeDeviceDetection(); 264} 265 266// Export for module systems 267if (typeof module !== 'undefined' && module.exports) { 268 module.exports = DeviceDetector; 269}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.