1/** 2 * SE (Sound Effect) foundation â provides window.SE.play(name). 3 * See docs/features/auction-bid-se/spec.md for design. 4 * 5 * API contract: all methods silent-catch every internal exception and never 6 * throw to the caller (callers still guard with typeof + try/catch for depth). 7 */ 8(function () { 9 'use strict'; 10 11 var LS_MUTE_KEY = 'se_muted'; 12 13 var audioContext = null; 14 // ä¸ä»£çªå·: AudioContext ãåçæãããã³ã« ++ãin-flight fetch ã® Promise 㯠15 // éå§æã®ä¸ä»£ãä¿æããresolve æã«ä¸ä»£ãé²ãã§ãããçµæãç ´æ£ããï¼closed race 対çï¼ 16 var contextGeneration = 0; 17 var seBuffers = {}; 18 var loadingPromises = {}; 19 20 function readConfig() { 21 var cfg = (typeof window !== 'undefined' && window.SE_CONFIG) || {}; 22 return { 23 files: cfg.files || {}, 24 volume: (typeof cfg.volume === 'number') ? cfg.volume : 1.0, 25 }; 26 } 27 28 function warnIfDebug(msg, err) { 29 try { 30 if (window.SE && window.SE.debug === true) { 31 console.warn('[SE]', msg, err || ''); 32 } 33 } catch (_) { /* silent */ } 34 } 35 36 function isMuted() { 37 try { 38 return typeof localStorage !== 'undefined' && localStorage.getItem(LS_MUTE_KEY) === '1'; 39 } catch (_) { return false; } 40 } 41 42 function mute() { 43 try { localStorage.setItem(LS_MUTE_KEY, '1'); } catch (_) { /* silent */ } 44 } 45 46 function unmute() { 47 try { localStorage.removeItem(LS_MUTE_KEY); } catch (_) { /* silent */ } 48 } 49 50 function ensureContext() { 51 var AC = window.AudioContext || window.webkitAudioContext; 52 if (!AC) return null; 53 54 if (audioContext && audioContext.state === 'closed') { 55 audioContext = null; 56 // closed æã¯ AudioBuffer ãã£ãã·ã¥ãç ´æ£ + ä¸ä»£ãé²ãã 57 // ï¼cross-context åçã® InvalidStateError åé¿ãin-flight fetch ã® resolve ç ´æ£ç¨ï¼ 58 seBuffers = {}; 59 loadingPromises = {}; 60 contextGeneration++; 61 } 62 if (!audioContext) { 63 try { audioContext = new AC(); } catch (e) { warnIfDebug('AudioContext init failed', e); return null; } 64 } 65 if (audioContext.state === 'suspended') { 66 try { audioContext.resume().catch(function (e) { warnIfDebug('resume rejected', e); }); } 67 catch (e) { warnIfDebug('resume throw', e); } 68 } 69 return audioContext; 70 } 71 72 function loadAndPlay(url, volume) { 73 var ctx = ensureContext(); 74 if (!ctx) return; 75 76 if (seBuffers[url]) { 77 playBuffer(ctx, seBuffers[url], volume); 78 return; 79 } 80 81 // åä¸ URL ã®ä¸¦å fetch 㯠1 æ¬ã«æãããéå§æã®ä¸ä»£ã Promise ã«åãè¾¼ã 82 if (!loadingPromises[url]) { 83 var startGen = contextGeneration; 84 loadingPromises[url] = fetch(url) 85 .then(function (res) { 86 if (!res.ok) throw new Error('fetch failed: ' + res.status); 87 return res.arrayBuffer(); 88 }) 89 .then(function (buf) { return ctx.decodeAudioData(buf); }) 90 .then(function (decoded) { 91 // ä¸ä»£ãé²ãã§ãããæ§ ctx ã® decode çµæãªã®ã§ãã£ãã·ã¥ã«æ»ããªã 92 if (startGen === contextGeneration) seBuffers[url] = decoded; 93 return decoded; 94 }) 95 .catch(function (e) { warnIfDebug('load failed: ' + url, e); return null; }) 96 // æåã»å¤±æãããã®å ´åã loadingPromises ããé¤å»ãã¦ã次å play æã«å試è¡ã§ããããã«ãã 97 .then(function (result) { delete loadingPromises[url]; return result; }, 98 function () { delete loadingPromises[url]; return null; }); 99 } 100 101 loadingPromises[url].then(function (decoded) { 102 // fetch å®äºæç¹ã§ context ãæ¢ã« closed ã«ãªã£ã¦ãããã¨ããããååå¾ã 103 var currentCtx = ensureContext(); 104 if (currentCtx && decoded) playBuffer(currentCtx, decoded, volume); 105 }).catch(function (e) { warnIfDebug('play chain failed', e); }); 106 } 107 108 // 鳿ºã fetch + decode ã㦠seBuffers ã«è¼ããã ã (åçããªã). 109 // Promise ãè¿ããå¼ã³åºãå´ã§ await å¯è½ãæ¢ãã¼ã or é²è¡ä¸ãªãæ¢å Promise ãè¿ã. 110 function preload(url) { 111 var ctx = ensureContext(); 112 if (!ctx || !url) return Promise.resolve(null); 113 if (seBuffers[url]) return Promise.resolve(seBuffers[url]); 114 if (loadingPromises[url]) return loadingPromises[url]; 115 var startGen = contextGeneration; 116 loadingPromises[url] = fetch(url) 117 .then(function (res) { 118 if (!res.ok) throw new Error('fetch failed: ' + res.status); 119 return res.arrayBuffer(); 120 }) 121 .then(function (buf) { return ctx.decodeAudioData(buf); }) 122 .then(function (decoded) { 123 if (startGen === contextGeneration) seBuffers[url] = decoded; 124 return decoded; 125 }) 126 .catch(function (e) { warnIfDebug('preload failed: ' + url, e); return null; }) 127 .then(function (result) { delete loadingPromises[url]; return result; }, 128 function () { delete loadingPromises[url]; return null; }); 129 return loadingPromises[url]; 130 } 131 132 // ååæå®ã§ preload ããï¼ensureLoaded ã®å®ä½ï¼. 133 // SE_CONFIG.files[name] ãç¡ã / mute ä¸ ã¯å³ resolve(null). 134 function ensureLoaded(name) { 135 try { 136 if (!name || isMuted()) return Promise.resolve(null); 137 var cfg = readConfig(); 138 var url = cfg.files[name]; 139 if (!url) return Promise.resolve(null); 140 return preload(url); 141 } catch (e) { 142 warnIfDebug('ensureLoaded failed', e); 143 return Promise.resolve(null); 144 } 145 } 146 147 // AudioContext ã使 + resume ããã ã. user gesture ä¸ã«å¼ã¶ãã¨ã§ iOS ã§ã unlock ããã. 148 // 鳿º preload ã¯è¡ããªãï¼å¿ è¦ã«ãªã£ãã¿ã¤ãã³ã°ã§ ensureLoaded ãå¼ã¶ï¼. 149 function unlock() { 150 try { ensureContext(); } catch (_) { /* silent */ } 151 } 152 153 function playBuffer(ctx, buffer, volume) { 154 try { 155 var source = ctx.createBufferSource(); 156 var gain = ctx.createGain(); 157 source.buffer = buffer; 158 gain.gain.value = (typeof volume === 'number' ? volume : 1.0); 159 source.connect(gain).connect(ctx.destination); 160 source.start(0); 161 } catch (e) { warnIfDebug('play failed', e); } 162 } 163 164 function play(name, volumeOverride) { 165 try { 166 if (!name) return; 167 if (isMuted()) return; 168 var cfg = readConfig(); 169 var url = cfg.files[name]; 170 if (!url) return; 171 var vol = (typeof volumeOverride === 'number') ? volumeOverride : cfg.volume; 172 loadAndPlay(url, vol); 173 } catch (e) { warnIfDebug('play top-level failed', e); } 174 } 175 176 // Public API â all methods contractually never throw 177 window.SE = { 178 play: play, 179 mute: mute, 180 unmute: unmute, 181 isMuted: isMuted, 182 unlock: unlock, 183 ensureLoaded: ensureLoaded, 184 // debug: true ã DevTools ã§ç«ã¦ã㨠console.warn ãæå¹åããã 185 }; 186 187 // iOS AudioContext unlock: åå clickï¼capture, onceï¼ã§ AudioContext ãçæã»resume ãã. 188 // 鳿º preload ã¯è¡ãããå¿ è¦ã«ãªã£ãã¿ã¤ãã³ã° (SE.ensureLoaded / SE.play) ã§ fetch ãã. 189 try { 190 var unlockHandler = function () { 191 unlock(); 192 // SE unlock ã¨åæã«åç»æ¼åºã®é³ãèªå ON. åç»å´ã¯ sessionStorage ãã©ã°ã§å¤å®ãã 193 // (auction-room.js:1746 wantSound = sessionStorage.getItem('auction:reveal-unmuted') === '1') 194 // 注: iOS Safari 㯠src 夿´æã« gesture å±¥æ´ããªã»ãããããã video ã®é³ä»ã autoplay 㯠195 // å®è³ªä¸å¯ (宿©ã§ priming/ç´æ¥ play() ãããã广ãªãç¢ºèªæ¸ã¿). Android / Desktop ã§ã¯å¹ã. 196 try { sessionStorage.setItem('auction:reveal-unmuted', '1'); } catch (_) { /* silent */ } 197 }; 198 document.addEventListener('click', unlockHandler, { once: true, capture: true }); 199 } catch (_) { /* silent â old browsers without addEventListener options object */ } 200 201 // ã¿ã復帰æã« AudioContext ã resume ãã. 202 // ããã¯ã°ã©ã¦ã³ãä¸ã« suspended ã«ãªã£ã AudioContext ããæ¬¡ã® SE.play ããåã«èµ°ããã. 203 // æªåæåç¶æ (unlock å) ã§ã¯è§¦ããªã (user gesture åã® new AudioContext ã¯è¦åã«ãªã). 204 try { 205 document.addEventListener('visibilitychange', function () { 206 if (document.visibilityState !== 'visible') return; 207 if (!audioContext) return; // æªåæåãªãä½ãããªã 208 if (audioContext.state === 'suspended') { 209 try { audioContext.resume().catch(function () {}); } catch (_) {} 210 } 211 }); 212 } catch (_) { /* silent */ } 213})();
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.