PageSourceSearch

https://www.ticketfairy.com/resources/js/home_new/device_detection.js?v=1790211420

js ticketfairy.com collected 2026-09-24 09:08:40 UTC 8,435 bytes, 239 lines download raw bytes

1/**
2 * Device Detection Utility
3 * 
4 * Reliable JS-only detection for iPad (especially iPadOS Safari masquerading as Mac),
5 * mobile phones, tablets, and desktop devices.
6 * 
7 * Features:
8 * - Detects device type: 'mobile' | 'tablet' | 'desktop'
9 * - Sets HTML class immediately: device-mobile / device-tablet / device-desktop
10 * - Exposes utilities: isIPad(), getDeviceType()
11 * - Sets cookies for PHP server-side detection
12 * - Provides debug output: window.__DEVICE_DEBUG__
13 * 
14 * This script runs immediately (synchronously) to set classes before animations initialize.
15 */
16(function() {
17    'use strict';
18
19    // Only run in browser environment
20    if (typeof navigator === 'undefined' || typeof document === 'undefined') {
21        return;
22    }
23
24    const ua = navigator.userAgent || '';
25    const maxTouchPoints = navigator.maxTouchPoints || 0;
26    
27    // Capability detection (pointer and hover media queries)
28    // Only use as supportive signal, not primary detection
29    let coarsePointer = false;
30    let noHover = false;
31    try {
32        coarsePointer = window.matchMedia && window.matchMedia('(pointer: coarse)').matches;
33        noHover = window.matchMedia && window.matchMedia('(hover: none)').matches;
34    } catch (e) {
35        // Media queries not supported, ignore
36    }
37
38    /**
39     * Detect if device is an iPad
40     * @returns {boolean} True if device is detected as iPad
41     */
42    function isIPad() {
43        // Classic iPad detection (older iOS)
44        const classicIPad = /iPad/.test(ua);
45        
46        // iPadOS masquerading as Mac detection
47        // iPadOS 13+ reports as Macintosh but has touch points
48        // Must check maxTouchPoints > 1 to avoid false positives on real Macs
49        const iPadOS = /Macintosh/.test(ua) && maxTouchPoints > 1;
50        
51        return classicIPad || iPadOS;
52    }
53
54    /**
55     * Detect if device is a phone (mobile)
56     * @returns {boolean} True if device is detected as mobile phone
57     */
58    function isMobile() {
59        // iPhone detection
60        const isIPhone = /iPhone|iPod/.test(ua);
61        
62        // Android phone detection (Android AND Mobile in UA)
63        const isAndroidPhone = /Android/.test(ua) && /Mobile/.test(ua);
64        
65        // Other mobile devices
66        const isOtherMobile = /webOS|BlackBerry|IEMobile|Opera Mini/i.test(ua);
67        
68        return isIPhone || isAndroidPhone || isOtherMobile;
69    }
70
71    /**
72     * Detect if device is a tablet
73     * @returns {boolean} True if device is detected as tablet
74     */
75    function isTablet() {
76        // iPad detection
77        if (isIPad()) {
78            return true;
79        }
80        
81        // Android tablet detection (Android but NOT Mobile)
82        const isAndroidTablet = /Android/.test(ua) && !/Mobile/.test(ua);
83        
84        // Capability fallback: only use if NOT a Mac (to avoid false positives)
85        // This catches edge cases but must not turn real Macs into tablets
86        const isMac = /Macintosh/.test(ua) && maxTouchPoints <= 1;
87        const capabilityFallback = !isMac && coarsePointer && noHover;
88        
89        return isAndroidTablet || capabilityFallback;
90    }
91
92    /**
93     * Get device type
94     * @returns {'mobile' | 'tablet' | 'desktop'} Device type
95     */
96    function getDeviceType() {
97        let device;
98        
99        // Normal detection logic
100        if (isMobile()) {
101            device = 'mobile';
102        } else if (isTablet()) {
103            device = 'tablet';
104        } else {
105            device = 'desktop';
106        }
107        
108        // Height-based fallback: treat desktop with small screen height as tablet
109        // This handles small laptops, compact desktop windows, and edge cases
110        // where desktop UX should behave like tablet UX
111        if (device === 'desktop' && window.innerHeight <= 800) {
112            device = 'tablet';
113        }
114        
115        return device;
116    }
117
118    /**
119     * Apply device class to HTML element
120     * This runs immediately to ensure classes are set before animations initialize
121     */
122    function applyDeviceClass() {
123        const deviceType = getDeviceType();
124        const htmlElement = document.documentElement;
125        
126        // Remove all device classes first
127        htmlElement.classList.remove('device-mobile', 'device-tablet', 'device-desktop');
128        
129        // Add the appropriate class
130        htmlElement.classList.add('device-' + deviceType);
131        
132        // Also set data attribute for easy access
133        htmlElement.setAttribute('data-device', deviceType);
134    }
135
136    /**
137     * Set or remove cookies for PHP server-side detection
138     */
139    function setDeviceCookies() {
140        const deviceType = getDeviceType();
141        const isTabletDevice = deviceType === 'tablet';
142        const cookiePath = '/';
143        const maxAge = 2592000; // 30 days in seconds
144        
145        // Add Secure flag if site is served over HTTPS
146        const isSecure = typeof window !== 'undefined' && 
147                         window.location && 
148                         window.location.protocol === 'https:';
149        const secureFlag = isSecure ? '; Secure' : '';
150        
151        // Set device type cookie
152        const deviceTypeCookie = 'app_device_type=' + deviceType + 
153                                 '; path=' + cookiePath + 
154                                 '; max-age=' + maxAge + 
155                                 '; SameSite=Lax' + secureFlag;
156        document.cookie = deviceTypeCookie;
157        
158        // Set/remove tablet cookie for backward compatibility
159        const tabletCookieName = 'app_device_tablet';
160        if (isTabletDevice) {
161            const tabletCookie = tabletCookieName + '=1; path=' + cookiePath + 
162                               '; max-age=' + maxAge + 
163                               '; SameSite=Lax' + secureFlag;
164            document.cookie = tabletCookie;
165        } else {
166            // Remove cookie by setting max-age=0
167            const removeCookie = tabletCookieName + '=; path=' + cookiePath + 
168                               '; max-age=0; SameSite=Lax' + secureFlag;
169            document.cookie = removeCookie;
170        }
171    }
172
173    /**
174     * Expose debug information
175     */
176    function exposeDebugInfo() {
177        window.__DEVICE_DEBUG__ = {
178            ua: ua,
179            maxTouchPoints: maxTouchPoints,
180            coarse: coarsePointer,
181            noHover: noHover,
182            screenHeight: window.innerHeight,
183            isIPad: isIPad(),
184            isMobile: isMobile(),
185            isTablet: isTablet(),
186            device: getDeviceType()
187        };
188    }
189
190    /**
191     * Expose utilities globally
192     */
193    function exposeUtilities() {
194        // Expose isIPad utility
195        window.isIPad = isIPad;
196        
197        // Expose getDeviceType utility
198        window.getDeviceType = getDeviceType;
199    }
200
201    // Run detection immediately (synchronously)
202    // This ensures classes are set before any animations or other scripts run
203    applyDeviceClass();
204    setDeviceCookies();
205    exposeDebugInfo();
206    exposeUtilities();
207
208    // Re-apply on orientation change (for mobile devices rotating)
209    window.addEventListener('orientationchange', function() {
210        // Small delay to allow orientation to settle
211        setTimeout(function() {
212            applyDeviceClass();
213            setDeviceCookies();
214            exposeDebugInfo();
215        }, 100);
216    });
217
218    // Re-apply on resize (for window resizing, but don't change device type on mobile rotation)
219    let resizeTimer;
220    window.addEventListener('resize', function() {
221        clearTimeout(resizeTimer);
222        resizeTimer = setTimeout(function() {
223            // Re-evaluate device type (height-based fallback may change device type)
224            // For mobile devices, device type should not change on rotation
225            const currentDevice = getDeviceType();
226            const htmlElement = document.documentElement;
227            const currentClass = htmlElement.classList.contains('device-mobile') ? 'mobile' :
228                                htmlElement.classList.contains('device-tablet') ? 'tablet' : 'desktop';
229            
230            // If device type changed (e.g., desktop -> tablet due to height change), re-apply
231            if (currentDevice !== currentClass) {
232                applyDeviceClass();
233                setDeviceCookies();
234                exposeDebugInfo();
235            }
236        }, 250);
237    });
238
239})();

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.