PageSourceSearch

https://kurokolabs.ai/js/lang-switcher.js?v=69aa29a5

js kurokolabs.ai collected 2026-10-05 12:10:33 UTC 5,474 bytes, 111 lines download raw bytes

1/**
2 * lang-switcher.js — animierter SVG-Sprachumschalter im "+"-Button (.sm-buttons).
3 *
4 * 1) Intro (einmal pro Session, nach dem Preloader): zeigt die zwei ANDEREN
5 *    Sprach-Flaggen (per aktueller Sprache geordnet) und blendet dann smooth in
6 *    das "+" über. Reines GSAP-Crossfade (opacity/scale) — kein MorphSVG nötig.
7 *      DE: 🇯🇵 → 🇬🇧 → +   |   JA: 🇬🇧 → 🇩🇪 → +   |   EN: 🇯🇵 → 🇩🇪 → +
8 * 2) Toggle: Klick/Tap auf das "+" öffnet/schließt die DE/JP/EN-Links. Bleibt
9 *    offen bis Tap außerhalb oder Auswahl.
10 *
11 * BUGFIX ggü. alter Lösung: Die Seite feuert via jQuery bei mouseenter/-leave
12 * JEDES <a> ein SYNTHETISCHES `$('.cursor').click()`. Der alte Outside-Click-
13 * Close interpretierte das als Außenklick → Menü schloss beim Mausbewegen.
14 * Fix: Öffnen nur bei `e.isTrusted`; Schließen über `pointerdown` (nicht click,
15 * da .click() kein pointerdown/mousedown auslöst) + isTrusted-Guard.
16 */
17(function () {
18  'use strict';
19
20  var FLAGS = ['kl-ls-japan', 'kl-ls-germany', 'kl-ls-uk'];
21  var ORDER = {
22    de: ['kl-ls-japan', 'kl-ls-uk'],      // JP → EN(UK) → +
23    ja: ['kl-ls-uk', 'kl-ls-germany'],    // EN(UK) → DE → +
24    en: ['kl-ls-japan', 'kl-ls-germany'], // JP → DE → +
25  };
26  var lang = (document.documentElement.getAttribute('lang') || 'de').toLowerCase().slice(0, 2);
27  var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
28
29  function byId(id) { return document.getElementById(id); }
30
31  function lockToPlus() {
32    FLAGS.forEach(function (id) { var g = byId(id); if (g) g.style.opacity = '0'; });
33    var plus = byId('kl-ls-plus');
34    if (!plus) return;
35    if (typeof gsap !== 'undefined') gsap.set(plus, { opacity: 1, scale: 1, rotation: 0, clearProps: 'transform' });
36    else plus.style.opacity = '1';
37  }
38
39  // ── Intro ──────────────────────────────────────────────────────────────────
40  var introStarted = false;
41  function playIntro() {
42    if (introStarted) return; introStarted = true;
43    var seen;
44    try { seen = sessionStorage.getItem('kl_lang_intro_seen'); } catch (_) {}
45    if (seen || reduced || typeof gsap === 'undefined' || !byId('kl-ls-plus')) { lockToPlus(); return; }
46
47    var order = ORDER[lang] || ORDER.de;
48    var plus = byId('kl-ls-plus');
49    gsap.set(plus, { opacity: 0 });
50    FLAGS.forEach(function (id) { var g = byId(id); if (g) gsap.set(g, { opacity: 0 }); });
51
52    var tl = gsap.timeline({
53      onComplete: function () {
54        try { sessionStorage.setItem('kl_lang_intro_seen', '1'); } catch (_) {}
55        lockToPlus();
56      },
57    });
58    var t = 0.15;
59    order.forEach(function (id, i) {
60      var g = byId(id);
61      if (!g) return;
62      var last = (i === order.length - 1);
63      tl.fromTo(g, { opacity: 0, scale: 0.7, transformOrigin: '50% 50%' },
64        { opacity: 1, scale: 1, duration: 0.42, ease: 'power2.out' }, t);
65      if (!last) {
66        tl.to(g, { opacity: 0, scale: 1.18, duration: 0.36, ease: 'power2.in' }, t + 0.66);
67        t += 0.86;
68      } else {
69        // Letzte Flagge wird horizontal zusammengequetscht (scaleX→0) → aus dem
70        // entstehenden vertikalen Streifen "springt" das "+" auf. Kein Einblenden,
71        // sondern eine Quetsch-Verwandlung Flagge → Plus.
72        var sq = t + 0.78;
73        tl.to(g, { scaleX: 0.05, scaleY: 0.9, opacity: 0, duration: 0.9, ease: 'power2.inOut', transformOrigin: '50% 50%' }, sq);
74        tl.fromTo(plus, { opacity: 0, scaleX: 0.05, scaleY: 0.85, transformOrigin: '50% 50%' },
75          { opacity: 1, scaleX: 1, scaleY: 1, duration: 0.62, ease: 'back.out(1.6)' }, sq + 0.52);
76      }
77    });
78  }
79
80  // Start nach dem Preloader (der deckt sonst die Animation ab). Auf Rückbesuchen
81  // ist der Preloader übersprungen (KL_FAST_PATH) → sofort. Fallback-Timeout, falls
82  // das Event nie kommt.
83  if (window.KL_FAST_PATH) playIntro();
84  window.addEventListener('kl:preloader-done', playIntro);
85  setTimeout(playIntro, 6500);
86
87  // ── Toggle (öffnen) ────────────────────────────────────────────────────────
88  document.addEventListener('click', function (e) {
89    if (e.isTrusted === false) return;                 // synthetisches .cursor.click() ignorieren
90    var plus = e.target && e.target.closest ? e.target.closest('.sm-button-plus') : null;
91    if (!plus) return;
92    e.preventDefault();
93    e.stopPropagation();                               // verwaiste IX2-Interaktion blocken
94    var box = document.querySelector('.sm-buttons');
95    if (box) box.classList.toggle('kl-sm-open');
96  }, true);
97
98  // ── Schließen (Außen-Pointerdown, immun gegen synthetische Klicks) ──────────
99  function closeOnOutside(e) {
100    if (e.isTrusted === false) return;
101    var open = document.querySelector('.sm-buttons.kl-sm-open');
102    if (!open) return;
103    if (!(e.target && e.target.closest && e.target.closest('.sm-buttons'))) open.classList.remove('kl-sm-open');
104  }
105  if ('PointerEvent' in window) {
106    document.addEventListener('pointerdown', closeOnOutside, true);
107  } else {
108    document.addEventListener('mousedown', closeOnOutside, true);
109    document.addEventListener('touchstart', closeOnOutside, true);
110  }
111})();

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.