PageSourceSearch

https://app.medi-one.ai/js/ios-pwa-fix.js

js medi-one.ai collected 2026-10-05 14:13:34 UTC 16,008 bytes, 401 lines download raw bytes

1/**
2 * iOS PWA-spezifische Fixes für Login und Touch-Events
3 * Behebt bekannte Probleme mit PWAs auf iOS Safari
4 */
5
6class IOSPWAFix {
7    static init() {
8        console.log('🍎 iOS PWA Fix wird initialisiert...');
9        
10        // Erkenne iOS und PWA-Modus
11        this.isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
12        this.isPWA = window.matchMedia('(display-mode: standalone)').matches || 
13                    window.navigator.standalone === true;
14        
15        console.log('🍎 iOS erkannt:', this.isIOS);
16        console.log('📱 PWA-Modus:', this.isPWA);
17        
18        if (this.isIOS) {
19            console.log('🔧 Aktiviere iOS-Fixes (PWA oder Safari)...');
20            this.applyIOSPWAFixes();
21            
22            // Spezielle Login-Fixes immer anwenden auf iOS
23            setTimeout(() => {
24                this.fixLoginForIOS();
25            }, 500);
26        }
27    }
28
29    static applyIOSPWAFixes() {
30        // Fix 1: Viewport-Lock für bessere Touch-Erfahrung
31        this.fixViewport();
32        
33        // Fix 2: Touch-Event-Optimierungen
34        this.fixTouchEvents();
35        
36        // Fix 3: LocalStorage-Persistenz verbessern
37        this.fixLocalStoragePersistence();
38        
39        // Fix 4: Modal-Fixes für iOS
40        this.fixModalsForIOS();
41        
42        // Fix 5: Form-Submission-Fixes
43        this.fixFormSubmissions();
44        
45        // Fix 6: Debug-Logging aktivieren
46        this.enableIOSDebugging();
47    }
48
49    static fixViewport() {
50        console.log('🔧 Optimiere Viewport für iOS PWA...');
51        
52        // WENIGER restriktiver Viewport für bessere Input-Kompatibilität
53        const viewport = document.querySelector('meta[name="viewport"]');
54        if (viewport) {
55            // Erlaube user-scalable für Input-Felder
56            viewport.setAttribute('content', 
57                'width=device-width, initial-scale=1.0, maximum-scale=2.0, user-scalable=yes, viewport-fit=cover'
58            );
59        }
60        
61        // Scrolling-Fixes NUR wenn nicht in Input-Feldern
62        document.body.style.webkitOverflowScrolling = 'touch';
63        // ENTFERNT: position: relative da es Input-Felder blockieren kann
64    }
65
66    static fixTouchEvents() {
67        console.log('🔧 Optimiere Touch-Events für iOS PWA...');
68        
69        // Verbesserte Touch-Erkennung für Buttons (NICHT für Input-Felder)
70        document.addEventListener('touchstart', function(e) {
71            // ÜBERSPRINGE Input-Felder, Textareas und Select-Elemente
72            if (e.target.matches('input, textarea, select')) {
73                return; // Lasse Input-Felder unberührt
74            }
75            
76            // Mark touch start for better button responsiveness
77            if (e.target.closest('button, .btn, [data-bs-toggle]')) {
78                e.target.closest('button, .btn, [data-bs-toggle]').classList.add('touching');
79            }
80        }, { passive: true });
81        
82        document.addEventListener('touchend', function(e) {
83            // ÜBERSPRINGE Input-Felder
84            if (e.target.matches('input, textarea, select')) {
85                return; // Lasse Input-Felder unberührt
86            }
87            
88            // Remove touch mark
89            const touchingElements = document.querySelectorAll('.touching');
90            touchingElements.forEach(el => el.classList.remove('touching'));
91        }, { passive: true });
92        
93        // Zusätzliche Click-Events für bessere Responsivität (NICHT für Input-Felder)
94        document.addEventListener('touchend', function(e) {
95            // ÜBERSPRINGE Input-Felder
96            if (e.target.matches('input, textarea, select')) {
97                return; // Lasse Input-Felder unberührt
98            }
99            
100            if (e.target.closest('.transcript-link, .btn')) {
101                // Füge kleine Verzögerung hinzu, um Doppel-Events zu vermeiden
102                setTimeout(() => {
103                    console.log('🍎 iOS Touch-Event verarbeitet für:', e.target);
104                }, 50);
105            }
106        }, { passive: true });
107    }
108
109    static fixLocalStoragePersistence() {
110        console.log('🔧 Verbessere LocalStorage-Persistenz für iOS PWA...');
111        
112        // Überwache LocalStorage-Änderungen
113        const originalSetItem = localStorage.setItem;
114        localStorage.setItem = function(key, value) {
115            try {
116                originalSetItem.call(this, key, value);
117                console.log('💾 iOS PWA: LocalStorage gesetzt:', key);
118            } catch (e) {
119                console.error('❌ iOS PWA: LocalStorage-Fehler:', e);
120                // Fallback für iOS Private-Modus
121                window.tempStorage = window.tempStorage || {};
122                window.tempStorage[key] = value;
123            }
124        };
125        
126        // Improved getItem for iOS
127        const originalGetItem = localStorage.getItem;
128        localStorage.getItem = function(key) {
129            try {
130                const value = originalGetItem.call(this, key);
131                if (value === null && window.tempStorage && window.tempStorage[key]) {
132                    return window.tempStorage[key];
133                }
134                return value;
135            } catch (e) {
136                console.error('❌ iOS PWA: LocalStorage-Lesefehler:', e);
137                return window.tempStorage ? window.tempStorage[key] : null;
138            }
139        };
140    }
141
142    static fixModalsForIOS() {
143        console.log('🔧 Optimiere Modals für iOS PWA...');
144        
145        // Überwache Modal-Events (WENIGER aggressiv für Input-Kompatibilität)
146        document.addEventListener('show.bs.modal', function(e) {
147            console.log('📱 iOS PWA: Modal wird geöffnet:', e.target.id);
148            
149            // WENIGER aggressive Body-Fixes - nur wenn nicht im Login-Bildschirm
150            const isLoginScreen = document.querySelector('#loginScreen, #loginScreen2:not(.d-none)');
151            if (!isLoginScreen) {
152                // Verhindere Body-Scrolling während Modal geöffnet ist
153                document.body.style.position = 'fixed';
154                document.body.style.top = `-${window.scrollY}px`;
155                document.body.style.width = '100%';
156            }
157        });
158        
159        document.addEventListener('hidden.bs.modal', function(e) {
160            console.log('📱 iOS PWA: Modal wurde geschlossen:', e.target.id);
161            
162            // Stelle Body-Scrolling wieder her (nur wenn es geändert wurde)
163            if (document.body.style.position === 'fixed') {
164                const scrollY = document.body.style.top;
165                document.body.style.position = '';
166                document.body.style.top = '';
167                document.body.style.width = '';
168                window.scrollTo(0, parseInt(scrollY || '0') * -1);
169            }
170        });
171    }
172
173    static fixFormSubmissions() {
174        console.log('🔧 Optimiere Formulare für iOS PWA...');
175        
176        // Verbesserte Form-Submission für iOS
177        document.addEventListener('submit', function(e) {
178            console.log('📝 iOS PWA: Form-Submission erkannt:', e.target.id);
179            
180            // Verhindere Doppel-Submissions
181            const submitButton = e.target.querySelector('button[type="submit"]');
182            if (submitButton && !submitButton.disabled) {
183                setTimeout(() => {
184                    submitButton.disabled = true;
185                    setTimeout(() => {
186                        submitButton.disabled = false;
187                    }, 2000);
188                }, 100);
189            }
190        });
191    }
192
193    static enableIOSDebugging() {
194        console.log('🔧 Aktiviere iOS PWA-Debugging...');
195        
196        // Erweiterte Fehlerbehandlung für iOS
197        window.addEventListener('error', function(e) {
198            console.error('❌ iOS PWA Error:', {
199                message: e.message,
200                filename: e.filename,
201                lineno: e.lineno,
202                colno: e.colno,
203                error: e.error,
204                stack: e.error?.stack,
205                userAgent: navigator.userAgent,
206                timestamp: new Date().toISOString()
207            });
208        });
209        
210        // Promise-Rejection-Handling
211        window.addEventListener('unhandledrejection', function(e) {
212            console.error('❌ iOS PWA Unhandled Promise Rejection:', {
213                reason: e.reason,
214                promise: e.promise,
215                timestamp: new Date().toISOString()
216            });
217        });
218        
219        // Network-Status überwachen
220        window.addEventListener('online', () => console.log('🌐 iOS PWA: Online'));
221        window.addEventListener('offline', () => console.log('🔌 iOS PWA: Offline'));
222    }
223
224    // Spezielle Login-Fixes für iOS PWA
225    static fixLoginForIOS() {
226        console.log('🔐 Aktiviere Login-Fixes für iOS PWA...');
227        
228        // Verbessere alle Input-Felder für iOS
229        this.enhanceInputFieldsForIOS();
230        
231        // Override des Login-Formulars für bessere iOS-Kompatibilität
232        const loginForm = document.getElementById('loginForm');
233        if (loginForm) {
234            loginForm.addEventListener('submit', function(e) {
235                console.log('🔐 iOS PWA: Login-Form-Submission');
236                
237                // Verbesserte Validierung für iOS
238                const email = loginForm.querySelector('#email');
239                const password = loginForm.querySelector('#password');
240                
241                if (email) {
242                    email.setAttribute('autocomplete', 'email');
243                    email.setAttribute('inputmode', 'email');
244                }
245                if (password) {
246                    password.setAttribute('autocomplete', 'current-password');
247                }
248                
249                // Sicherstellen, dass Inputs den Fokus verlieren
250                document.activeElement.blur();
251            });
252        }
253    }
254
255    // Verbessere alle Input-Felder für iOS PWA
256    static enhanceInputFieldsForIOS() {
257        console.log('🔧 Verbessere Input-Felder für iOS PWA...');
258        
259        const inputs = document.querySelectorAll('input, textarea');
260        inputs.forEach(input => {
261            // Entferne störende CSS-Eigenschaften
262            input.style.webkitAppearance = 'none';
263            input.style.appearance = 'none';
264            
265            // Verbessere Touch-Verhalten
266            input.style.webkitTouchCallout = 'default';
267            input.style.webkitUserSelect = 'text';
268            input.style.userSelect = 'text';
269            
270            // iOS-spezifische Attribute
271            if (input.type === 'email') {
272                input.setAttribute('inputmode', 'email');
273                input.setAttribute('autocapitalize', 'none');
274            }
275            if (input.type === 'password') {
276                input.setAttribute('autocapitalize', 'none');
277            }
278            
279            // Focus-Events für bessere iOS-Kompatibilität
280            input.addEventListener('focus', function() {
281                console.log('🍎 Input focused:', input.id || input.name);
282                // Stelle sicher, dass der Input sichtbar ist
283                setTimeout(() => {
284                    input.scrollIntoView({ behavior: 'smooth', block: 'center' });
285                }, 300);
286            });
287            
288            input.addEventListener('blur', function() {
289                console.log('🍎 Input blurred:', input.id || input.name);
290            });
291        });
292        
293        // Überwache dynamisch hinzugefügte Input-Felder
294        const observer = new MutationObserver((mutations) => {
295            mutations.forEach((mutation) => {
296                mutation.addedNodes.forEach((node) => {
297                    if (node.nodeType === 1) { // Element node
298                        const newInputs = node.querySelectorAll ? node.querySelectorAll('input, textarea') : [];
299                        newInputs.forEach(input => {
300                            console.log('🍎 Neues Input-Feld erkannt:', input);
301                            this.enhanceInputFieldsForIOS();
302                        });
303                    }
304                });
305            });
306        });
307        
308        observer.observe(document.body, { childList: true, subtree: true });
309    }
310
311    // Cache-Management für iOS PWA
312    static clearIOSCache() {
313        console.log('🧹 Lösche iOS PWA Cache...');
314        
315        if ('serviceWorker' in navigator) {
316            navigator.serviceWorker.getRegistrations().then(function(registrations) {
317                for(let registration of registrations) {
318                    registration.unregister();
319                    console.log('🗑️ Service Worker unregistriert');
320                }
321            });
322        }
323        
324        if ('caches' in window) {
325            caches.keys().then(function(names) {
326                for(let name of names) {
327                    caches.delete(name);
328                    console.log('🗑️ Cache gelöscht:', name);
329                }
330            });
331        }
332        
333        // LocalStorage leeren (mit Bestätigung)
334        if (confirm('Möchten Sie auch die lokalen Daten löschen? Dies wird Sie abmelden.')) {
335            localStorage.clear();
336            sessionStorage.clear();
337            console.log('🗑️ LocalStorage geleert');
338        }
339    }
340}
341
342// Exportiere für globale Verfügbarkeit
343window.IOSPWAFix = IOSPWAFix;
344
345// Auto-Initialize mit Input-Field-Priorität
346if (document.readyState === 'loading') {
347    document.addEventListener('DOMContentLoaded', () => {
348        // Priorisiere Input-Field-Fixes
349        if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
350            IOSPWAFix.enhanceInputFieldsForIOS();
351        }
352        IOSPWAFix.init();
353    });
354} else {
355    // Sofortige Input-Field-Fixes wenn DOM bereits geladen
356    if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
357        IOSPWAFix.enhanceInputFieldsForIOS();
358    }
359    IOSPWAFix.init();
360}
361
362// Notfall-Fix für Login-Felder
363window.fixLoginInputs = function() {
364    console.log('🔧 Notfall-Fix für Login-Inputs...');
365    const emailInput = document.getElementById('email');
366    const passwordInput = document.getElementById('password');
367    
368    [emailInput, passwordInput].forEach(input => {
369        if (input) {
370            input.style.webkitAppearance = 'none';
371            input.style.appearance = 'none';
372            input.style.webkitUserSelect = 'text';
373            input.style.userSelect = 'text';
374            input.style.pointerEvents = 'auto';
375            input.style.touchAction = 'manipulation';
376            input.removeAttribute('readonly');
377            input.removeAttribute('disabled');
378            
379            console.log('🔧 Input-Feld repariert:', input.id);
380        }
381    });
382};
383
384// Debug-Funktion für Entwicklung
385window.debugIOSPWA = function() {
386    console.log('🍎 iOS PWA Debug Info:', {
387        isIOS: IOSPWAFix.isIOS,
388        isPWA: IOSPWAFix.isPWA,
389        userAgent: navigator.userAgent,
390        standalone: window.navigator.standalone,
391        displayMode: window.matchMedia('(display-mode: standalone)').matches,
392        localStorage: {
393            available: typeof Storage !== 'undefined',
394            userToken: !!localStorage.getItem('userToken')
395        },
396        serviceWorker: 'serviceWorker' in navigator,
397        cache: 'caches' in window
398    });
399};
400
401console.log('🍎 iOS PWA Fix geladen - verwende window.debugIOSPWA() für Debugging'); 

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.