PageSourceSearch

https://meteozentrale.de/assets/pwa/install-widget.js?ver=1.7.0-1786651033

js meteozentrale.de collected 2026-09-25 05:01:49 UTC 8,679 bytes, 129 lines download raw bytes

1(function(){
2  const MARKER = 'MZ-PWA-INSTALL-WIDGET-V5';
3  if (window.__mzPwaInstallWidgetLoaded) return;
4  window.__mzPwaInstallWidgetLoaded = true;
5
6  const cfg = window.MZ_PWA_CONFIG || {};
7  const storageKey = 'mz_pwa_install_widget_closed_until';
8  const appUrl = cfg.appUrl || '/app/';
9  const manifestUrl = cfg.manifestUrl || '/manifest.webmanifest';
10  const swUrl = cfg.swUrl || '/webpushr-sw.js';
11  const scope = cfg.scope || '/';
12  const forceShow = !!cfg.forceShow;
13  const showDelay = Number(cfg.showDelay || 900);
14  const statsEndpoint = cfg.statsEndpoint || '/app/pwa-install-counter.php';
15  const idKey = 'mz_pwa_install_id_v1';
16
17  let deferredPrompt = null;
18  let widgetShownTracked = false;
19
20  function isStandalone(){ return (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) || window.navigator.standalone === true; }
21  function isIOS(){ return /iphone|ipad|ipod/i.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); }
22  function now(){ return Date.now(); }
23  function closed(){ if (forceShow) return false; try { return Number(localStorage.getItem(storageKey) || 0) > now(); } catch(e){ return false; } }
24  function rememberClose(hours){ try { localStorage.setItem(storageKey, String(now() + hours*3600*1000)); } catch(e){} }
25  function source(){ return new URLSearchParams(location.search).get('source') || (location.pathname.startsWith('/app') ? 'app' : 'site'); }
26  function uid(){
27    try { let v = localStorage.getItem(idKey); if (!v) { v = (crypto && crypto.randomUUID) ? crypto.randomUUID() : ('mz_' + Date.now() + '_' + Math.random().toString(16).slice(2)); localStorage.setItem(idKey, v); } return v; }
28    catch(e){ return 'no_storage_' + Math.random().toString(16).slice(2); }
29  }
30  function track(event){
31    try { fetch(statsEndpoint, {method:'POST', headers:{'Content-Type':'application/json'}, keepalive:true, body:JSON.stringify({event, id:uid(), source:source(), path:location.pathname + location.search})}).catch(()=>{}); } catch(e) {}
32  }
33  function shouldShowHome(){ return !location.pathname.replace(/\/index\.php$/,'/').startsWith('/app/') && (isStandalone() || /pwa_hub|pwa|app/i.test(location.search) || cfg.forceHomeButton); }
34
35  function ensureHead(){
36    if (!document.querySelector('link[rel="manifest"]')) { const l=document.createElement('link'); l.rel='manifest'; l.href=manifestUrl; document.head.appendChild(l); }
37    if (!document.querySelector('meta[name="theme-color"]')) { const m=document.createElement('meta'); m.name='theme-color'; m.content='#07111f'; document.head.appendChild(m); }
38    if (!document.querySelector('link[rel="apple-touch-icon"]')) { const a=document.createElement('link'); a.rel='apple-touch-icon'; a.href='/assets/pwa/icons/meteozentrale-app-apple-180.png'; document.head.appendChild(a); }
39  }
40  function registerSW(){
41    if ('serviceWorker' in navigator) {
42      window.addEventListener('load', () => { navigator.serviceWorker.register(swUrl, {scope}).catch(err => console.warn('Meteozentrale PWA Service Worker:', err)); }, {once:true});
43    }
44  }
45  function html(){
46    return `
47      <div class="mz-pwa-widget mz-pwa-widget-v5" id="mz-pwa-widget" hidden data-marker="${MARKER}">
48        <div class="mz-pwa-card" role="region" aria-label="Meteozentrale App installieren">
49          <div class="mz-pwa-dot"><span>📡</span></div>
50          <div class="mz-pwa-copy"><div class="mz-pwa-title">Meteozentrale als App nutzen</div><div class="mz-pwa-sub">SkyGuard, Wetternews, Regen, Sturm und Modelle direkt vom Startbildschirm öffnen.</div></div>
51          <div class="mz-pwa-actions"><button class="mz-pwa-install" type="button" id="mz-pwa-install-btn">App installieren</button><button class="mz-pwa-close" type="button" id="mz-pwa-close" aria-label="Hinweis schließen">×</button></div>
52        </div>
53      </div>
54      <button class="mz-pwa-mini" id="mz-pwa-mini" type="button" hidden>📲 App</button>
55      <a class="mz-pwa-home" id="mz-pwa-home" href="${appUrl}" hidden aria-label="Zur Meteozentrale App-Zentrale">← App-Zentrale</a>
56      <div class="mz-pwa-modal" id="mz-pwa-modal" hidden role="dialog" aria-modal="true" aria-labelledby="mz-pwa-modal-title">
57        <div class="mz-pwa-modal-card">
58          <div class="mz-pwa-modal-head"><div><h2 id="mz-pwa-modal-title">Meteozentrale-App installieren</h2><p>Aktuelle Installationshilfe für Chrome, Edge und Safari. Falls ein Browser „veraltet“ meldet: Seite neu laden und in Chrome/Safari öffnen.</p></div><button class="mz-pwa-modal-close" id="mz-pwa-modal-close" type="button" aria-label="Schließen">×</button></div>
59          <div class="mz-pwa-modal-body">
60            <div class="mz-pwa-step"><span>📱</span><div><b>
60Android / Chrome / Edge:</b><br>Browsermenü öffnen und „App installieren“ oder „Zum Startbildschirm hinzufügen“ wählen.</div></div>
61            <div class="mz-pwa-step"><span>🍎</span><div><b>iPhone / iPad:</b><br>In Safari das Teilen-Symbol öffnen und „Zum Home-Bildschirm“ antippen.</div></div>
62            <div class="mz-pwa-step"><span>💻</span><div><b>Desktop:</b><br>Chrome oder Edge zeigen die Installation meist rechts in der Adressleiste oder im Menü an.</div></div>
63            <div class="mz-pwa-step mz-pwa-private"><span>🕵️</span><div><b>Privatfenster:</b><br>Dort wird Installation oft nicht angeboten. Bitte normal testen.</div></div>
64          </div>
65          <div class="mz-pwa-modal-foot">Direkter Installationslink: <b>${appUrl}</b> · Hinweis: In-App-Browser von Facebook können Installation und Scrollen blockieren.</div>
66        </div>
67      </div>`;
68  }
69  function inject(){
70    if (document.getElementById('mz-pwa-widget')) return;
71    document.body.insertAdjacentHTML('beforeend', html());
72    const widget = document.getElementById('mz-pwa-widget');
73    const mini = document.getElementById('mz-pwa-mini');
74    const modal = document.getElementById('mz-pwa-modal');
75    const installBtn = document.getElementById('mz-pwa-install-btn');
76    const closeBtn = document.getElementById('mz-pwa-close');
77    const modalClose = document.getElementById('mz-pwa-modal-close');
78    const home = document.getElementById('mz-pwa-home');
79
80    function showModal(){ modal.hidden = false; }
81    function hideModal(){ modal.hidden = true; }
82    function showWidget(){
83      if (isStandalone()) return;
84      if (closed()) { mini.hidden = false; return; }
85      widget.hidden = false;
86      if (!widgetShownTracked) { widgetShownTracked = true; track('prompt_shown'); }
87    }
88    function hideWidget(hours){ widget.hidden = true; mini.hidden = false; if (hours) { rememberClose(hours); track('install_dismiss'); } }
89    function showHome(){ if (shouldShowHome()) home.hidden = false; }
90
91    installBtn.addEventListener('click', async () => {
92      track('install_click');
93      if (deferredPrompt) {
94        try { deferredPrompt.prompt(); await deferredPrompt.userChoice; } catch(e) {}
95        deferredPrompt = null;
96      } else {
97        showModal();
98      }
99    });
100    mini.addEventListener('click', () => { mini.hidden = true; widget.hidden = false; if (!widgetShownTracked) { widgetShownTracked = true; track('prompt_shown'); } });
101    closeBtn.addEventListener('click', () => hideWidget(6));
102    modalClose.addEventListener('click', hideModal);
103    modal.addEventListener('click', e => { if (e.target === modal) hideModal(); });
104    document.addEventListener('keydown', e => { if (e.key === 'Escape') hideModal(); });
105    home.addEventListener('click', () => track('app_home_click'));
106
107    if (isStandalone()) { showHome(); track('standalone_ping'); setInterval(() => track('standalone_ping'), 5*60*1000); return; }
108    setTimeout(showWidget, showDelay);
109    showHome();
110  }
111
112  window.addEventListener('beforeinstallprompt', (event) => {
113    event.preventDefault();
114    deferredPrompt = event;
115    const btn = document.getElementById('mz-pwa-install-btn'); if (btn) btn.textContent = 'App installieren';
116    const widget = document.getElementById('mz-pwa-widget');
117    if (widget && !closed() && !isStandalone()) { widget.hidden = false; if (!widgetShownTracked) { widgetShownTracked = true; track('prompt_shown'); } }
118  });
119  window.addEventListener('appinstalled', () => {
120    rememberClose(24*365);
121    track('install_success');
122    const widget = document.getElementById('mz-pwa-widget'); const mini = document.getElementById('mz-pwa-mini');
123    if (widget) widget.hidden = true; if (mini) mini.hidden = true;
124  });
125
126  ensureHead();
127  registerSW();
128  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', inject, {once:true}); else inject();
129})();

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.