PageSourceSearch

https://www.myownmusic.de/include/js/mom-softnav.js?v=1786167064

js myownmusic.de collected 2026-10-02 04:46:38 UTC 9,961 bytes, 212 lines download raw bytes

1/**
2 * MOM Soft-Navigation (Player V3, Stage 2)
3 * ---------------------------------------------------------------------------
4 * Lückenlose Wiedergabe beim Seitenwechsel: solange Musik läuft, werden
5 * interne Link-Klicks abgefangen, die Zielseite per fetch geladen und nur
6 * #totalcontent getauscht — das <audio>-Element (und der Mini-Player im
7 * Header) leben weiter. JEDER Fehler fällt auf normale Navigation zurück.
8 *
9 * Kill-Switch: Cookie mom_softnav=0.
10 */
11(function (window, $) {
12    'use strict';
13    if (!window.MOM || !window.MOM.audio) return;
14    if (window.MOM.softnav) return;
15
16    var busy = false;
17
18    function enabled() {
19        return document.cookie.indexOf('mom_softnav=0') === -1;
20    }
21
22    function playing() {
23        try { return !window.MOM.audio.element().paused; } catch (e) { return false; }
24    }
25
26    /* ------------------------------------------------------------ Klick-Filter */
27    var SKIP_PATH = /(\/account\/logout|\/player\/(download|deliver)|\/register\/login_fb)/;
28    var SKIP_EXT  = /\.(pdf|zip|mp3|jpe?g|png|gif|svg|xml|rss|css|js)(\?|$)/i;
29
30    function interceptable(a, e) {
31        if (e.defaultPrevented) return false;                    // andere Handler (Popups etc.) haben übernommen
32        if (e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return false;
33        var href = a.getAttribute('href');
34        if (!href || href.charAt(0) === '#') return false;
35        if (/^(javascript:|mailto:|tel:)/i.test(href)) return false;
36        if (a.hasAttribute('download')) return false;
37        var target = a.getAttribute('target');
38        if (target && target !== '_self') return false;
39        if (a.getAttribute('onclick')) return false;             // Inline-Handler (openPlayer/openInlinebox …)
40        var url;
41        try { url = new URL(a.href, window.location.href); } catch (err) { return false; }
42        if (url.origin !== window.location.origin) return false;
43        if (SKIP_PATH.test(url.pathname)) return false;
44        if (SKIP_EXT.test(url.pathname)) return false;
45        if (url.search.indexOf('ajax_load') !== -1) return false;
46        if (url.href === window.location.href) return false;
47        return true;
48    }
49
50    document.addEventListener('click', function (e) {
51        if (!enabled() || !playing() || busy) return;
52        var a = e.target && e.target.closest ? e.target.closest('a[href]') : null;
53        if (!a || !interceptable(a, e)) return;
54        e.preventDefault();
55        softLoad(a.href, true);
56    }, false);
57
58    window.addEventListener('popstate', function () {
59        if (!enabled()) return;
60        if (playing() && !busy) softLoad(window.location.href, false);
61        else window.location.reload();
62    });
63
64    /* ------------------------------------------------------- Assets nachladen */
65    function loadedUrls(selector, attr) {
66        var set = {};
67        document.querySelectorAll(selector).forEach(function (el) {
68            try { set[new URL(el.getAttribute(attr), window.location.href).href] = true; } catch (e) {}
69        });
70        return set;
71    }
72
73    function injectAssets(doc) {
74        // Stylesheets: fehlende einfach anhängen (Reihenfolge unkritisch für Zusatz-Styles)
75        var haveCss = loadedUrls('link[rel="stylesheet"]', 'href');
76        doc.querySelectorAll('link[rel="stylesheet"][href]').forEach(function (l) {
77            var abs;
78            try { abs = new URL(l.getAttribute('href'), window.location.href).href; } catch (e) { return; }
79            if (!haveCss[abs]) {
80                var el = document.createElement('link');
81                el.rel = 'stylesheet'; el.href = abs;
82                document.head.appendChild(el);
83                haveCss[abs] = true;
84            }
85        });
86
87        // Skripte (head + body, z. B. js_library/jcarousel, Play-Page-Module):
88        // fehlende sequenziell laden, damit Abhängigkeiten stimmen
89        var haveJs = loadedUrls('script[src]', 'src');
90        var queue = [];
91        doc.querySelectorAll('script[src]').forEach(function (s) {
92            var abs;
93            try { abs = new URL(s.getAttribute('src'), window.location.href).href; } catch (e) { return; }
94            if (!haveJs[abs]) { queue.push(abs); haveJs[abs] = true; }
95        });
96
97        return queue.reduce(function (p, src) {
98            return p.then(function () {
99                return new Promise(function (resolve, reject) {
100                    var el = document.createElement('script');
101                    el.src = src;
102                    el.onload = resolve;
103                    el.onerror = reject;
104                    document.head.appendChild(el);
105                });
106            });
107        }, Promise.resolve());
108    }
109
110    /* ------------------------------------- Inline-Skripte im Content ausführen */
111    function execScriptsIn(container) {
112        var scripts = container.querySelectorAll('script');
113        return Array.prototype.reduce.call(scripts, function (p, old) {
114            return p.then(function () {
115                return new Promise(function (resolve, reject) {
116                    var el = document.createElement('script');
117                    for (var i = 0; i < old.attributes.length; i++)
118                        el.setAttribute(old.attributes[i].name, old.attributes[i].value);
119                    if (old.src) {
120                        el.onload = resolve; el.onerror = reject;
121                        old.parentNode.replaceChild(el, old);
122                    } else {
123                        el.textContent = old.textContent;
124                        old.parentNode.replaceChild(el, old);
125                        resolve();
126                    }
127                });
128            });
129        }, Promise.resolve());
130    }
131
132    /* -------------------------------------------------------------- Ladebalken */
133    function progress(on) {
134        document.documentElement.classList.toggle('mom-softnav-loading', !!on);
135    }
136
137    /* --------------------------------------------------------------- Kernflow */
138    function softLoad(url, push) {
139        busy = true;
140        progress(true);
141
142        fetch(url, { credentials: 'same-origin', redirect: 'follow', headers: { 'X-MOM-SoftNav': '1' } })
143            .then(function (r) {
144                if (!r.ok) throw new Error('http ' + r.status);
145                var ct = r.headers.get('content-type') || '';
146                if (ct.indexOf('text/html') === -1) throw new Error('not html');
147                // Redirects übernehmen (z. B. forward()) — aber NIE cross-origin weiterverarbeiten
148                if (r.url) {
149                    if (new URL(r.url, window.location.href).origin !== window.location.origin)
150                        throw new Error('cross-origin redirect');
151                    url = r.url;
152                }
153                return r.text();
154            })
155            .then(function (html) {
156                var doc = new DOMParser().parseFromString(html, 'text/html');
157                var fresh = doc.getElementById('totalcontent');
158                if (!fresh) throw new Error('no totalcontent');
159
160                return injectAssets(doc).then(function () {
161                    var cur = document.getElementById('totalcontent');
162                    if (!cur) throw new Error('no local totalcontent');
163
164                    // altes Play-Page-Modul sauber lösen (Waveform-Listener etc.)
165                    if (window.MOM.playPage && window.MOM.playPage.teardown) {
166                        try { window.MOM.playPage.teardown(); } catch (e) {}
167                    }
168
169                    // Vertrauensgrenze: fresh stammt aus einem same-origin-Fetch unserer
170                    // EIGENEN serverseitig gerenderten Seite (Origin + Redirect geprüft) —
171                    // identisches Vertrauensniveau wie eine normale Voll-Navigation.
172                    cur.innerHTML = fresh.innerHTML;
173                    document.title = doc.title;
174
175                    // Meta-Description/Canonical bewusst nicht synchronisiert (SPA-Zwischenzustand)
176
177                    return execScriptsIn(cur).then(function () {
178                        // Seiten-Init der NEUEN Seite ausführen (definiert __momPageInit neu
179                        // und ruft es via jQuery(fn) sofort auf, da DOM bereits ready ist).
180                        // Vertrauensgrenze: das Skript ist der vom eigenen Server emittierte
181                        // #mom-page-init-Block der Zielseite (same-origin geprüft) — bei einer
182                        // normalen Navigation würde der Browser exakt denselben Code ausführen.
183                        // Es wird nichts in den Skripttext interpoliert.
184                        var init = doc.getElementById('mom-page-init');
185                        if (init && init.textContent) {
186                            (new Function(init.textContent))();
187                        } else if (window.__momPageInit) {
188                            window.__momPageInit = function () {};
189                        }
190                    });
191                }).then(function () {
192                    if (push) {
193                        try { window.history.pushState({ momSoft: 1 }, '', url); } catch (e) {}
194                    }
195                    window.scrollTo(0, 0);
196                    // Quick-Messenger sofort füllen: die Box kam frisch mit dem Swap und
197                    // zeigt sonst bis zum nächsten 15s-Cron-Tick nur den Lade-Spinner
198                    try { if (window.__momCron && typeof window.browserCron_ajax === 'function') window.browserCron_ajax(); } catch (e) {}
199                    progress(false);
200                    busy = false;
201                });
202            })
203            .catch(function () {
204                progress(false);
205                busy = false;
206                window.location.href = url;   // Fallback: harte Navigation
207            });
208    }
209
210    window.MOM.softnav = { load: softLoad, enabled: enabled };
211
212})(window, jQuery);

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.