PageSourceSearch

https://www.ugglarpcamping.se/wp-content/themes/winternet/assets/js/standard-animation.min.js?ver=1782977681

js ugglarpcamping.se collected 2026-10-10 01:19:33 UTC 18,789 bytes, 467 lines download raw bytes

1document.addEventListener('DOMContentLoaded', function () {
2    (() => {
3        "use strict";
4
5        /******************************************************************
6         * A) Delad gate: whenPageEnterEnd()
7         ******************************************************************/
8        function whenPageEnterEndFactory() {
9            let resolveFn;
10            const p = new Promise((r) => (resolveFn = r));
11            return {
12                promise: p,
13                resolve: () => resolveFn?.(),
14            };
15        }
16
17        // global-ish (på window för säker delning över moduler)
18        const PAGE_ENTER = (window.__PAGE_ENTER__ ||= whenPageEnterEndFactory());
19
20        const prefersReduced =
21            window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
22
23        const waitForAnim = (el, durMs) =>
24            new Promise((resolve) => {
25                let done = false;
26                const off = () => {
27                    if (done) return;
28                    done = true;
29                    el.removeEventListener("animationend", onEnd, true);
30                    clearTimeout(timer);
31                    resolve();
32                };
33                const onEnd = (e) => { if (e.target === el) off(); };
34                const timer = setTimeout(off, durMs + 50);
35                el.addEventListener("animationend", onEnd, true);
36            });
37
38        /******************************************************************
39         * B) Motion-lager: flytta allt som ska animeras i ett eget lager
40         ******************************************************************/
41        function ensureMotionLayer() {
42            const wrap = document.querySelector('.page-transition');
43            if (!wrap) return null;
44
45            // Redan strukturerad?
46            let motion = wrap.querySelector(':scope > .page-transition__motion');
47            if (motion) return motion;
48
49            // Skapa lagret
50            motion = document.createElement('div');
51            motion.className = 'page-transition__motion';
52
53            // Vad ska INTE flyttas in i motion-lagret?
54            const EXCLUDE_SEL = [
55                'header.is-fixed',
56                '.site-nav.is-fixed',
57                '[data-stay-fixed]',
58                '[data-no-motion]',
59                '.clip-path-header',        // <-- NYTT: låt clip-path header stå utanför
60                '.page-transition__motion'
61            ].join(',');
62
63            // Flytta alla barn som inte matchar EXCLUDE_SEL
64            const children = Array.from(wrap.children);
65            children.forEach(el => {
66                if (!el.matches(EXCLUDE_SEL)) motion.appendChild(el);
67            });
68
69            // Sätt in motion-lagret först
70            wrap.prepend(motion);
71
72            return motion;
73        }
74
75        // Kör direkt:
76        const motionLayer = ensureMotionLayer();
77
78        /******************************************************************
79         * C) Page transition modul
80         ******************************************************************/
81        (function pageTransitionModule() {
82            const WRAP_SEL = ".page-transition";
83            const ENTER_CLASS = "is-entering";
84            const LEAVE_CLASS = "is-leaving";
85            const DURATION_MS = 220;
86            const NO_TRANSITION_ATTR = "data-no-transition";
87
88            const wrap = document.querySelector(WRAP_SEL);
89
90            // Om transition saknas → släpp innehållet direkt efter DOM-ready.
91            if (!wrap) {
92                setTimeout(PAGE_ENTER.resolve, 0);
93                return;
94            }
95
96            const runEnter = async () => {
97                if (!prefersReduced) {
98                    wrap.classList.remove(LEAVE_CLASS);
99
100                    // Se till att preload-state är aktivt när vi triggar animationen
101                    if (!wrap.classList.contains("is-preload")) {
102                        wrap.classList.add("is-preload");
103                    }
104
105                    // Force reflow så browsern hinner "ta" preload-läget
106                    void wrap.offsetWidth;
107
108                    // Starta enter-animationen
109                    wrap.classList.add(ENTER_CLASS);
110                    await waitForAnim(wrap, DURATION_MS);
111                    wrap.classList.remove(ENTER_CLASS);
112                }
113
114                // När enter-animationen är klar → ta bort preload
115                wrap.classList.remove("is-preload");
116
117                PAGE_ENTER.resolve();
118                if (window.ScrollTrigger) {
119                    requestAnimationFrame(() => window.ScrollTrigger.refresh());
120                }
121            };
122
123            const runLeave = async (navigate) => {
124                if (!prefersReduced) {
125                    wrap.classList.add(LEAVE_CLASS);
126                    await waitForAnim(wrap, DURATION_MS);
127                }
128                navigate();
129            };
130
131            // Init ENTER
132            if (document.readyState === "loading") {
133                document.addEventListener("DOMContentLoaded", runEnter, { once: true });
134            } else {
135                runEnter();
136            }
137
138            // bfcache
139            window.addEventListener("pageshow", (e) => {
140                if (e.persisted) {
141                    // skapa en NY gate för nästa cykel
142                    window.__PAGE_ENTER__ = whenPageEnterEndFactory();
143                    // uppdatera vår referens
144                    Object.assign(PAGE_ENTER, window.__PAGE_ENTER__);
145                    runEnter();
146                }
147            });
148
149            // Link-delegation
150            document.addEventListener("click", (e) => {
151                if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
152                const a = e.target.closest("a");
153                if (!a) return;
154                if (a.closest('[data-archive-filter-nav="1"]')) return;
155                if (a.hasAttribute(NO_TRANSITION_ATTR)) return;
156
157                const href = a.getAttribute("href");
158                if (!href) return;
159
160                const url = new URL(a.href, window.location.href);
161                if (a.target && a.target !== "_self") return;
162                if (a.hasAttribute("download")) return;
163                if (url.origin !== window.location.origin) return;
164                if (href.startsWith("mailto:") || href.startsWith("tel:")) return;
165                if (url.pathname === window.location.pathname && url.hash && url.hash !== "#") return;
166
167                e.preventDefault();
168                runLeave(() => { window.location.href = url.href; });
169            }, { capture: true });
170        })();
171
172        /******************************************************************
173         * D) Sequential modul – väntar på PAGE_ENTER.promise
174         ******************************************************************/
175        (function sequentialModule() {
176            // Viktigt: dynamisk flagga (kan dyka upp sent)
177            const isSeqEnabled = () => !!(window.w_functions && window.w_functions.sequentialAnimations);
178
179            // anti-hopp: lägg på inert tidigt
180            const markInert = () => {
181                document.querySelectorAll(".sequential").forEach((el) => {
182                    if (!el.classList.contains("sequential-disabled")) el.classList.add("sequential-inert");
183                });
184            };
185            const clearInert = () => {
186                document.querySelectorAll(".sequential.sequential-inert").forEach((el) => el.classList.remove("sequential-inert"));
187            };
188
189            // === Helpers (GSAP + fallback) ===
190            function initSequentialOnce(scroller) {
191                // Startläge för alla (bara en gång – respektera redan initierade)
192                const fresh = Array.from(document.querySelectorAll('.sequential:not([data-seq-init])'));
193                fresh.forEach(el => {
194                    el.setAttribute('data-seq-init', '1');
195                });
196                // sätt startläge i ett svep (effektivare)
197                if (fresh.length && typeof gsap !== "undefined") gsap.set(fresh, { autoAlpha: 0, y: 24 });
198
199                // Batcha nya och tidigare oinitierade element
200                if (typeof ScrollTrigger !== "undefined") {
201                    ScrollTrigger.batch('.sequential', {
202                        start: "top 95%",
203                        scroller: scroller || null,
204                        once: true,
205                        onEnter: (batch) => {
206                            // ta bort inert på de som faktiskt ska in just nu
207                            batch.forEach(el => el.classList.remove('sequential-inert'));
208
209                            gsap.to(batch, {
210                                autoAlpha: 1,
211                                y: 0,
212                                duration: 0.42,
213                                ease: "power2.out",
214                                stagger: 0.045,
215                                overwrite: true,
216                                onComplete: () => {
217                                    // rensa inline-styles för ren layout efteråt
218                                    batch.forEach(el => {
219                                        el.classList.add('revealed');
220                                        gsap.set(el, { clearProps: "opacity,visibility,transform" });
221                                    });
222                                }
223                            });
224                        }
225                    });
226                }
227            }
228
229            function refreshScrollTriggersAfterImages(container = document) {
230                requestAnimationFrame(() => {
231                    const imgs = Array.from(container.querySelectorAll('img'));
232                    if (!imgs.length) { window.ScrollTrigger?.refresh(); return; }
233                    let pending = imgs.length;
234                    const done = () => { if (--pending <= 0) window.ScrollTrigger?.refresh(); };
235                    imgs.forEach(img => {
236                        if (img.complete) done();
237                        else {
238                            img.addEventListener('load', done, { once: true });
239                            img.addEventListener('error', done, { once: true });
240                        }
241                    });
242                });
243            }
244
245            function setupSequentialMultipleObserver() {
246                const elements = document.querySelectorAll(".sequential-multiple");
247                if (!elements.length) return;
248                const observer = new IntersectionObserver((entries, obs) => {
249                    entries.forEach((entry, index) => {
250                        if (entry.isIntersecting) {
251                            const el = entry.target;
252                            if (!el.classList.contains("animation-done")) {
253                                if (typeof gsap !== 'undefined') {
254                                    gsap.fromTo(el, { y: 24, opacity: 0 }, { y: 0, opacity: 1, duration: 0.35, ease: "power2.out", delay: 0.045 * index });
255                                } else {
256                                    el.style.opacity = 1;
257                                    el.style.transform = "translateY(0)";
258                                }
259                                el.classList.add("animation-done");
260                            }
261                            obs.unobserve(el);
262                        }
263                    });
264                }, { threshold: 0.1 });
265                elements.forEach(el => observer.observe(el));
266            }
267
268            const HAS_GSAP = () => (typeof window.gsap !== "undefined" && typeof window.ScrollTrigger !== "undefined");
269
270            const reinit = () => {
271                if (!isSeqEnabled()) { // avstängt → visa direkt
272                    document.querySelectorAll(".sequential").forEach(el => el.classList.add("sequential-disabled"));
273                    clearInert();
274                    return;
275                }
276                if (!HAS_GSAP()) { // GSAP saknas → fallback + ta bort inert
277                    clearInert();
278                    setupSequentialMultipleObserver();
279                    return;
280                }
281                gsap.registerPlugin(ScrollTrigger);
282                // sätt startläge för nytillkomna innan vi batchar
283                const newOnes = Array.from(document.querySelectorAll('.sequential:not([data-seq-init])'));
284                if (newOnes.length) gsap.set(newOnes, { autoAlpha: 0, y: 24 });
285
286                initSequentialOnce();
287                refreshScrollTriggersAfterImages(document);
288                setupSequentialMultipleObserver();
289            };
290
291            // inert tidigt
292            if (document.readyState === "loading") {
293                document.addEventListener("DOMContentLoaded", markInert, { once: true });
294            } else {
295                markInert();
296            }
297
298            // Vänta bara på sidövergången, inte på alla bilder/iframes.
299            PAGE_ENTER.promise.then(() => {
300                reinit();
301            });
302
303            // bfcache
304            window.addEventListener("pageshow", (e) => {
305                if (e.persisted) reinit();
306            });
307
308            // WooCommerce + AJAX
309            if (window.jQuery) {
310                const $ = window.jQuery;
311                $(document.body).on(
312                    'wc_fragments_loaded wc_fragments_refreshed updated_wc_div ' +
313                    'added_to_cart removed_from_cart cart_page_refreshed price_slider_slide',
314                    reinit
315                );
316                $(document).on(
317                    'found_variation show_variation hide_variation reset_data woocommerce_update_variation_values',
318                    reinit
319                );
320                $(document).ajaxComplete(reinit);
321            } else {
322                const container = document.querySelector('.single-product') || document.body;
323                const mo = new MutationObserver(() => {
324                    reinit();
325                    refreshScrollTriggersAfterImages(container);
326                });
327                mo.observe(container, { childList: true, subtree: true });
328            }
329
330            // Om config/flaggan kommer sent (t.ex. i footer) → enkel poll
331            let tries = 0;
332            const waitForConfig = setInterval(() => {
333                tries++;
334                if (isSeqEnabled() || tries > 40) { // 40 x 50ms ≈ 2s
335                    clearInterval(waitForConfig);
336                    reinit(); // kör om när flaggan blivit sann (eller ge upp tyst)
337                }
338            }, 50);
339
340            // Om du kan trigga ett event när w_functions sätts
341            window.addEventListener('w_functions_ready', reinit);
342        })();
343
344        /******************************************************************
345         * E) Clip Path Animation – väntar på PAGE_ENTER
346         ******************************************************************/
347        (function clipPathModule() {
348            const container = document.querySelector(".clip-path-header");
349            if (!container) return;
350            if (typeof gsap === "undefined" || typeof ScrollTrigger === "undefined") return;
351
352            const pageGate = window.__PAGE_ENTER__ || { promise: Promise.resolve() };
353
354            pageGate.promise.then(() => {
355                let tl = gsap.timeline({
356                    scrollTrigger: {
357                        trigger: container,
358                        pin: true,
359                        scrub: 2,
360                        start: "top top",
361                        end: "+=5000"
362                    }
363                })
364                    .to(".clip-path-header figure", {
365                        clipPath: "circle(100%)"
366                    });
367            });
368        })();
369
370    })();
371});
372
373
374
375
376//Clip Path Animation
377document.addEventListener("DOMContentLoaded", function () {
378    "use strict";
379
380    const container = document.querySelector(".clip-path-header");
381    if (!container) return; // avbryt om inte elementet finns
382
383    let tl = gsap.timeline({
384        scrollTrigger: {
385            trigger: container,
386            pin: true,
387            scrub: 2,
388            start: "top top",
389            end: "+=5000"
390        }
391    })
392        .to(".clip-path-header figure", {
393            clipPath: "circle(100%)"
394        });
395});
396
397
398
399// Scale Animation
400// Scale Animation (bidirectional scrub – alltid upp & ner)
401document.addEventListener("DOMContentLoaded", () => {
402    "use strict";
403    //gsap.registerPlugin(ScrollTrigger);
404
405    const container = document.querySelector(".scale-header");
406    if (!container) return;
407
408    // Minska iOS-jitter vid adressfälts-resizes
409    ScrollTrigger.config({ ignoreMobileResize: true });
410
411    const targets = document.querySelectorAll(".scale-header img, .scale-header video");
412    if (!targets.length) return;
413
414    // --- CSS sätter redan startläge: scale(0.6) translateY(24px) + opacity:0 ---
415    // Intro på load: fade + lyft till y:0 (påverkar inte scroll-scalingen)
416    window.addEventListener("load", () => {
417        gsap.to(targets, {
418            autoAlpha: 1,
419            y: 0,
420            duration: 0.6,
421            ease: "power2.out",
422            overwrite: "auto"
423        });
424    });
425
426    // Scroll-scaling: från CSS scale(0.6) → scale(1) (scrubbar fritt åt båda håll)
427    const tl = gsap.timeline({ ease: "none" })
428        .to(targets, {
429            scale: 1,
430            duration: 1,
431            force3D: true
432        })
433        .to({}, { duration: 1 }); // liten buffert för mjuk pin
434
435    const makeTrigger = (opts) => ScrollTrigger.create({
436        trigger: ".scale-header",
437        start: "top top",
438        end: opts.end,
439        pin: true,
440        scrub: opts.scrub,          // numerisk scrub = inbyggd smoothing vid snabb scroll
441        anticipatePin: 1,
442        invalidateOnRefresh: true,
443        animation: tl,
444
445        // Viktigt: inga låsningar – låt scrubbning styra båda håll
446        // Du kan tillfälligt felsöka med markers: true
447        // markers: true
448    });
449
450    // Breakpoints
451    ScrollTrigger.matchMedia({
452        "(max-width: 767px)": function () {
453            makeTrigger({ end: "+=80%", scrub: 0.35 });
454        },
455        "(min-width: 768px) and (max-width: 1023px)": function () {
456            makeTrigger({ end: "+=100%", scrub: 0.45 });
457        },
458        "(min-width: 1024px)": function () {
459            makeTrigger({ end: "+=120%", scrub: 0.6 });
460        }
461    });
462
463    // Orientation-fix
464    window.addEventListener("orientationchange", () => {
465        setTimeout(() => ScrollTrigger.refresh(), 200);
466    });
467});

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.