PageSourceSearch

https://stelliverse.fr/sfx.js

js stelliverse.fr collected 2026-09-25 14:52:43 UTC 4,127 bytes, 141 lines download raw bytes

1(function () {
2  if (window.startradSfx) return;
3
4  var ctx = null;
5  var enabled = true;
6  var unlocked = false;
7
8  try {
9    var stored = localStorage.getItem('startrad_sound');
10    if (stored !== null) {
11      enabled = !(stored === '0' || stored === 'off' || stored === 'false');
12    }
13  } catch (e) {}
14
15  function getCtx() {
16    if (!ctx) {
17      var Ctx = window.AudioContext || window.webkitAudioContext;
18      if (!Ctx) return null;
19      ctx = new Ctx();
20    }
21    return ctx;
22  }
23
24  function emit(c, freq, dur, type, gain) {
25    var now = c.currentTime;
26    var duration = Number(dur || 0.06);
27    var volume = Math.max(0.0002, Number(gain || 0.04));
28    var osc = c.createOscillator();
29    var amp = c.createGain();
30    osc.type = type || 'sine';
31    osc.frequency.value = Number(freq || 440);
32    amp.gain.setValueAtTime(0.0001, now);
33    amp.gain.exponentialRampToValueAtTime(volume, now + 0.01);
34    amp.gain.exponentialRampToValueAtTime(0.0001, now + duration);
35    osc.connect(amp);
36    amp.connect(c.destination);
37    osc.start(now);
38    osc.stop(now + duration);
39  }
40
41  function unlockAudio() {
42    try {
43      var c = getCtx();
44      if (!c) return;
45      if (c.state === 'running') {
46        unlocked = true;
47        return;
48      }
49      var resumed = c.resume();
50      if (resumed && typeof resumed.then === 'function') {
51        resumed.then(function () { unlocked = true; }).catch(function () {});
52      } else {
53        unlocked = true;
54      }
55    } catch (e) {}
56  }
57
58  function play(freq, dur, type, gain) {
59    if (!enabled) return;
60    try {
61      var c = getCtx();
62      if (!c) return;
63      var run = function () {
64        emit(c, freq, dur, type, gain);
65      };
66      if (c.state === 'suspended') {
67        var resumed = c.resume();
68        if (resumed && typeof resumed.then === 'function') {
69          resumed.then(function () {
70            unlocked = true;
71            run();
72          }).catch(function () {});
73        } else {
74          run();
75        }
76        return;
77      }
78      run();
79    } catch (e) {}
80  }
81
82  var api = {
83    hover: function () { play(880, 0.045, 'sine', 0.022); },
84    click: function () { play(520, 0.09, 'triangle', 0.08); },
85    tick: function () { play(1200, 0.035, 'square', 0.02); },
86    toggle: function () {
87      play(420, 0.055, 'sine', 0.045);
88      setTimeout(function () { play(720, 0.055, 'sine', 0.038); }, 40);
89    },
90    setEnabled: function (value) {
91      enabled = !!value;
92      try { localStorage.setItem('startrad_sound', enabled ? 'on' : 'off'); } catch (e) {}
93    },
94    isEnabled: function () {
95      return enabled;
96    }
97  };
98
99  window.startradSfx = api;
100
101  var hoverSelector = 'a, button, [role="button"], .faq-item, .install-step';
102  var clickSelector = 'a.btn, button.btn, a.cta, [data-sfx-click]';
103  var lastHoverTarget = null;
104  var lastHoverTs = 0;
105  var unlockOptions = { passive: true, capture: true };
106
107  document.addEventListener('pointerdown', unlockAudio, unlockOptions);
108  document.addEventListener('touchstart', unlockAudio, unlockOptions);
109  document.addEventListener('keydown', unlockAudio, true);
110  document.addEventListener('mousedown', unlockAudio, unlockOptions);
111
112  document.addEventListener('mouseover', function (event) {
113    var target = event.target;
114    if (!target || typeof target.closest !== 'function') return;
115    var interactive = target.closest(hoverSelector);
116    if (!interactive) return;
117    var rel = event.relatedTarget;
118    if (rel && typeof interactive.contains === 'function' && interactive.contains(rel)) return;
119
120    var now = (window.performance && typeof window.performance.now === 'function')
121      ? window.performance.now()
122      : Date.now();
123    if (interactive === lastHoverTarget && now - lastHoverTs < 70) return;
124
125    lastHoverTarget = interactive;
126    lastHoverTs = now;
127    api.hover();
128  }, true);
129
130  document.addEventListener('click', function (event) {
131    var target = event.target;
132    if (!target || typeof target.closest !== 'function') return;
133    if (target.closest(clickSelector)) {
134      api.click();
135    }
136  }, true);
137
138  if (!unlocked) {
139    unlockAudio();
140  }
141})();

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.