PageSourceSearch

https://shimeji.app/mascot.js?v=20260928201838

js shimeji.app collected 2026-10-02 01:14:52 UTC 5,917 bytes, 161 lines download raw bytes

1// Shimeji minimal JS: share, related, install ribbon
2(function () {
3  const byId = (id) => document.getElementById(id);
4  const q = (sel, el = document) => el.querySelector(sel);
5  const qall = (sel, el = document) => Array.from(el.querySelectorAll(sel));
6  const debounce = (fn, ms = 120) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); }; };
7
8  // Python-like slugify for URLs (match server):
9  function jsSlugify(str) {
10    if (!str) return '';
11    let s = str.toLowerCase().replace(/&/g, ' and ');
12    s = s.replace(/[^a-z0-9]+/g, '-');
13    s = s.replace(/-+/g, '-').replace(/^-|-$/g, '');
14    return s;
15  }
16
17  // -------- A) Homepage Share button --------
18  (function(){
19    const btn = byId('share-home');
20    if (!btn) return;
21    btn.addEventListener('click', async () => {
22      const url = location.origin + '/';
23      const title = 'Shimeji - Anime Screen Buddies for Android';
24      const text = 'Let anime characters roam your Android screen with Shimeji!';
25      if (navigator.share) {
26        try { await navigator.share({ title, text, url }); return; } catch(_) {}
27      }
28      const t = encodeURIComponent(text + ' ' + url);
29      window.open('https://twitter.com/intent/tweet?text=' + t, '_blank', 'noopener');
30    });
31  })();
32
33  // -------- D) Share (mascot page) --------
34  (function () {
35    const shareBtn = byId('share-btn');
36    if (!shareBtn) return;
37
38    const url   = shareBtn.dataset.shareUrl || location.href;
39    const title = shareBtn.dataset.shareTitle || 'Shimeji';
40    const text  = shareBtn.dataset.shareText  || '';
41
42    const showToast = (msg) => {
43      let t = byId('toast');
44      if (!t) {
45        t = document.createElement('div');
46        t.id = 'toast';
47        t.className = 'toast';
48        document.body.appendChild(t);
49      }
50      t.textContent = msg;
51      t.classList.add('show');
52      setTimeout(() => t.classList.remove('show'), 1800);
53    };
54
55    if (!navigator.share && !navigator.clipboard) {
56      shareBtn.style.display = 'none';
57      return;
58    }
59
60    shareBtn.addEventListener('click', async () => {
61      if (window.isSecureContext && navigator.share) {
62        try { await navigator.share({ title, text, url }); return; }
63        catch (_) {}
64      }
65      try { await navigator.clipboard.writeText(url); showToast('Link copied'); }
66      catch (_) {
67        const ta = document.createElement('textarea');
68        ta.value = url; ta.style.position = 'fixed'; ta.style.opacity = '0';
69        document.body.appendChild(ta); ta.select();
70        try { document.execCommand('copy'); showToast('Link copied'); }
71        catch (_) { window.prompt('Copy this link:', url); }
72        document.body.removeChild(ta);
73      }
74    });
75  })();
76
77  // -------- E) Related (mascot page) --------
78  if (window.__SHIMEJI__) {
79    try {
80      const { id, catSlug } = window.__SHIMEJI__;
81      fetch(`/${catSlug}/`).then(r => r.text()).then(html => {
82        const div = document.createElement('div'); div.innerHTML = html;
83        const cards = Array.from(div.querySelectorAll('.grid .card'));
84        const out = [];
85        for (const a of cards) {
86          const href = a.getAttribute('href') || '';
87          const idFromHref = parseInt(href.split('-').pop()) || -1;
88          if (idFromHref === id) continue;
89          out.push(a.outerHTML);
90          if (out.length >= 8) break;
91        }
92        const mount = byId('related-grid'); if (mount) mount.innerHTML = out.join('');
93      }).catch(()=>{});
94    } catch(_) {}
95  }
96
97  // -------- F) Install ribbon --------
98  (function(){
99    const ribbon = byId('install-ribbon');
100    const closeBtn = byId('ribbon-close');
101    if (!ribbon || !closeBtn) return;
102
103    const key = 'shimeji_hide_ribbon_until';
104    const now = Date.now();
105    const hideUntil = parseInt(localStorage.getItem(key) || '0', 10);
106    const mobile = window.matchMedia('(max-width: 820px)').matches;
107
108    if (mobile && now > hideUntil) {
109      requestAnimationFrame(() => ribbon.classList.add('show'));
110    }
111
112    closeBtn.addEventListener('click', () => {
113      ribbon.classList.remove('show');
114      ribbon.style.display = 'none';
115      localStorage.setItem(key, String(now + 7*24*60*60*1000)); // 7 days
116    });
117  })();
118
119// -------- H) Smart "Get this character" CTA (deep link if app, Play fallback) --------
120(function () {
121  const btn = document.querySelector('.js-get');
122  if (!btn) return;
123
124  const cid  = btn.getAttribute('data-cid') || '';
125  const name = btn.getAttribute('data-name') || 'Shimeji';
126
127  // Build Play URL with Install Referrer
128  const play = new URL('https://play.google.com/store/apps/details');
129  play.searchParams.set('id', 'com.digitalcosmos.shimeji');
130  play.searchParams.set('referrer', `cid=${encodeURIComponent(cid)}&src=website`);
131
132  // Android Chrome-style intent (opens app if installed; Play fallback embedded)
133  const intent = `intent://shimeji.app/dl?cid=${encodeURIComponent(cid)}`
134               + `#Intent;scheme=https;package=com.digitalcosmos.shimeji;`
135               + `S.browser_fallback_url=${encodeURIComponent(play.toString())};end`;
136
137  const ua = navigator.userAgent || '';
138  const isAndroid    = /Android/i.test(ua);
139  const isChromeLike = isAndroid && /Chrome|Chromium|EdgA|SamsungBrowser/i.test(ua) && !/OPR|Opera/i.test(ua);
140
141  const go = (href) => { try { location.href = href; } catch (_) { location.assign(href); } };
142
143  btn.addEventListener('click', (e) => {
144    e.preventDefault();
145
146    try { if (window.gtag) gtag('event','click_get_character',{ cid, name, path: location.pathname }); } catch(_) {}
147
148    if (isChromeLike) {
149      // Android Chrome/Samsung/Edge → use intent://
150      go(intent);
151    } else if (isAndroid) {
152      // Other Android browsers (e.g., Firefox) → just use Play with referrer
153      go(play.toString());
154    } else {
155      // Desktop/iOS/etc. → Play (Android-only app)
156      go(play.toString());
157    }
158  });
159})();
160
161})();

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.