PageSourceSearch

https://dynamic-salmiakki-095693.netlify.app/device-detector.js

js dynamic-salmiakki-095693.netlify.app collected 2026-10-03 10:40:17 UTC 9,558 bytes, 269 lines download raw bytes

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.