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.