1(function(){ 2 function formatTime(seconds){ 3 if(!isFinite(seconds) || seconds < 0) return '00:00'; 4 var m = Math.floor(seconds / 60); 5 var s = Math.floor(seconds % 60); 6 return String(m).padStart(2,'0') + ':' + String(s).padStart(2,'0'); 7 } 8 function triggerDownload(url){ 9 var a = document.createElement('a'); 10 a.href = url; 11 a.setAttribute('download', ''); 12 a.style.display = 'none'; 13 document.body.appendChild(a); 14 a.click(); 15 setTimeout(function(){ a.remove(); }, 1000); 16 } 17 function initDemoSounds(){ 18 var filters = Array.prototype.slice.call(document.querySelectorAll('[data-demo-filter]')); 19 var cards = Array.prototype.slice.call(document.querySelectorAll('.demo-sound-card')); 20 var search = document.querySelector('#demo-sound-search'); 21 var empty = document.querySelector('.demo-empty-state'); 22 var activeFilter = 'all'; 23 24 function applyFilters(){ 25 var q = search ? search.value.trim().toLowerCase() : ''; 26 var visible = 0; 27 cards.forEach(function(card){ 28 var type = card.getAttribute('data-demo-type') || ''; 29 var text = card.getAttribute('data-demo-search') || card.textContent.toLowerCase(); 30 var matchesFilter = activeFilter === 'all' || type === activeFilter; 31 var matchesSearch = !q || text.indexOf(q) !== -1; 32 var show = matchesFilter && matchesSearch; 33 card.hidden = !show; 34 if(show) visible++; 35 }); 36 if(empty) empty.hidden = visible !== 0; 37 } 38 filters.forEach(function(btn){ 39 btn.addEventListener('click', function(){ 40 activeFilter = btn.getAttribute('data-demo-filter') || 'all'; 41 filters.forEach(function(b){ b.classList.remove('is-active'); }); 42 btn.classList.add('is-active'); 43 applyFilters(); 44 }); 45 }); 46 if(search) search.addEventListener('input', applyFilters); 47 48 var players = Array.prototype.slice.call(document.querySelectorAll('.demo-mini-player')); 49 var audios = players.map(function(player){ return player.querySelector('audio'); }).filter(Boolean); 50 players.forEach(function(player){ 51 var audio = player.querySelector('audio'); 52 var btn = player.querySelector('.demo-play-toggle'); 53 var time = player.querySelector('.demo-time'); 54 var waveform = player.querySelector('.demo-waveform'); 55 if(!audio || !btn) return; 56 function setProgress(){ 57 var pct = audio.duration ? (audio.currentTime / audio.duration) * 100 : 0; 58 player.style.setProperty('--progress', pct + '%'); 59 if(time) time.textContent = formatTime(audio.currentTime || audio.duration || 0); 60 } 61 btn.addEventListener('click', function(){ 62 if(audio.paused){ audios.forEach(function(other){ if(other !== audio) other.pause(); }); audio.play(); } 63 else audio.pause(); 64 }); 65 if(waveform){ waveform.addEventListener('click', function(e){ 66 if(!audio.duration) return; 67 var rect = waveform.getBoundingClientRect(); 68 var pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)); 69 audio.currentTime = audio.duration * pct; 70 setProgress(); 71 }); } 72 audio.addEventListener('loadedmetadata', function(){ if(time) time.textContent = formatTime(audio.duration); }); 73 audio.addEventListener('timeupdate', setProgress); 74 audio.addEventListener('play', function(){ player.classList.add('is-playing'); }); 75 audio.addEventListener('pause', function(){ player.classList.remove('is-playing'); }); 76 audio.addEventListener('ended', function(){ player.classList.remove('is-playing'); player.style.setProperty('--progress', '0%'); }); 77 }); 78 79 var config = window.OnCueDemoDownloads || {}; 80 var modal = document.querySelector('.oncue-download-modal'); 81 var form = modal ? modal.querySelector('.oncue-download-form') : null; 82 var close = modal ? modal.querySelector('.oncue-download-modal__close') : null; 83 var emailInput = form ? form.querySelector('input[name="email"]') : null; 84 var consentInput = form ? form.querySelector('input[name="consent"]') : null; 85 var honeypotInput = form ? form.querySelector('input[name="website"]') : null; 86 var submitBtn = form ? form.querySelector('button[type="submit"]') : null; 87 var pendingRequest = false;
88 var consentLabel = form ? form.querySelector('.oncue-consent span') : null; 89 var text = modal ? modal.querySelector('.oncue-download-modal__text') : null; 90 var selectionText = modal ? modal.querySelector('.oncue-download-modal__selection') : null; 91 var error = form ? form.querySelector('.oncue-download-error') : null; 92 var pending = null; 93 94 if(consentLabel && config.consentText) consentLabel.textContent = config.consentText; 95 if(text && config.permissionText) text.textContent = config.permissionText; 96 97 // Multi-select 98 var selectionBar = document.querySelector('.demo-selection-bar'); 99 var selectionCount = selectionBar ? selectionBar.querySelector('.demo-selection-count') : null; 100 var selectionClear = selectionBar ? selectionBar.querySelector('.demo-selection-clear') : null; 101 var selectionDownload = selectionBar ? selectionBar.querySelector('.demo-selection-download') : null; 102 103 function selectedItems(){ 104 return Array.prototype.slice.call(document.querySelectorAll('.demo-select-box:checked')).map(function(box){ 105 var card = box.closest('.demo-sound-card'); 106 var btn = card ? card.querySelector('.demo-download') : null; 107 if(!btn) return null; 108 return { url: btn.getAttribute('data-download-url'), soundId: btn.getAttribute('data-sound-id') || '', title: btn.getAttribute('data-sound-title') || '' }; 109 }).filter(Boolean); 110 } 111 function updateSelectionBar(){ 112 if(!selectionBar) return; 113 var n = document.querySelectorAll('.demo-select-box:checked').length; 114 selectionBar.hidden = n === 0; 115 if(selectionCount) selectionCount.textContent = n + ' selected'; 116 if(selectionDownload) selectionDownload.textContent = n > 1 ? 'Download ' + n + ' sounds' : 'Download selected'; 117 } 118 function clearSelection(){ 119 Array.prototype.slice.call(document.querySelectorAll('.demo-select-box:checked')).forEach(function(box){ 120 box.checked = false; 121 var card = box.closest('.demo-sound-card'); 122 if(card) card.classList.remove('is-selected'); 123 }); 124 updateSelectionBar(); 125 } 126 document.addEventListener('change', function(e){ 127 if(!e.target.classList || !e.target.classList.contains('demo-select-box')) return; 128 var card = e.target.closest('.demo-sound-card'); 129 if(card) card.classList.toggle('is-selected', e.target.checked); 130 updateSelectionBar(); 131 }); 132 if(selectionClear) selectionClear.addEventListener('click', clearSelection); 133 if(selectionDownload) selectionDownload.addEventListener('click', function(){ 134 var items = selectedItems(); 135 if(items.length) openModal({ multi: items }); 136 }); 137 138 function openModal(data){ 139 pending = data; 140 if(!modal || !form || config.requireEmail === false){ registerAndDownload('', false, data); return; } 141 if(error){ error.hidden = true; error.textContent = ''; } 142 if(emailInput) emailInput.value = ''; 143 if(consentInput) consentInput.checked = false; 144 if(selectionText){ 145 if(data.multi && data.multi.length > 1){ 146 var titles = data.multi.map(function(it){ return it.title; }).filter(Boolean); 147 var shown = titles.slice(0, 4).join(', ') + (titles.length > 4 ? ' +' + (titles.length - 4) + ' more' : ''); 148 selectionText.textContent = data.multi.length + ' sounds: ' + shown; 149 selectionText.hidden = false; 150 } else { 151 selectionText.hidden = true; 152 } 153 } 154 modal.hidden = false; 155 modal.setAttribute('aria-hidden','false'); 156 setTimeout(function(){ if(emailInput) emailInput.focus(); }, 50); 157 } 158 function closeModal(){ 159 if(!modal) return; 160 modal.hidden = true; 161 modal.setAttribute('aria-hidden','true'); 162 pending = null; 163 } 164 if(close) close.addEventListener('click', closeModal); 165 if(modal) modal.addEventListener('click', function(e){ if(e.target === modal) closeModal(); }); 166 167 function setPending(state){ 168 pendingRequest = state; 169 if(submitBtn){ 170 submitBtn.disabled = state; 171 if(state){ submitBtn.dataset.label = submitBtn.textContent; submitBtn.textContent = 'Preparingâ¦'; } 172 else if(submitBtn.dataset.label){ submitBtn.textContent = submitBtn.dataset.label; } 173 } 174 } 175 function registerOne(email, consent, item){ 176 if(!config.ajaxUrl || !config.nonce) return Promise.resolve(item.url); 177 var body = new FormData(); 178 body.append('action','oncue_register_download'); 179 body.append('nonce',config.nonce); 180 body.append('email',email || ''); 181 body.append('consent',consent ? '1' : '0'); 182 body.append('website', honeypotInput ? honeypotInput.value : ''); 183 body.append('url',item.url); 184 body.append('sound_id',item.soundId || ''); 185 body.append('sound_title',item.title || ''); 186 return fetch(config.ajaxUrl, { method:'POST', credentials:'same-origin', body:body }) 187 .then(function(r){ return r.json(); }) 188 .then(function(json){ 189 if(!json || !json.success) throw new Error((json && json.data && json.data.message) || 'Download failed'); 190 return (json.data && json.data.url) || item.url; 191 }); 192 } 193 function registerAndDownload(email, consent, data){ 194 if(!data || pendingRequest) return; 195 var items = (data.multi ? data.multi.slice() : [data]).filter(function(it){ return it && it.url; }); 196 if(!items.length) return; 197 setPending(true); 198 var chain = Promise.resolve();
199 items.forEach(function(item, i){ 200 chain = chain.then(function(){ return registerOne(email, consent, item); }) 201 .then(function(url){ 202 triggerDownload(url); 203 if(i < items.length - 1) return new Promise(function(res){ setTimeout(res, 600); }); 204 }); 205 }); 206 chain 207 .then(function(){ closeModal(); clearSelection(); }) 208 .catch(function(err){ 209 if(error && modal && !modal.hidden){ error.textContent = err.message || 'Download failed'; error.hidden = false; } 210 }) 211 .finally(function(){ setPending(false); }); 212 } 213 if(form){ form.addEventListener('submit', function(e){ 214 e.preventDefault(); 215 registerAndDownload(emailInput ? emailInput.value : '', consentInput && consentInput.checked, pending); 216 }); } 217 218 Array.prototype.slice.call(document.querySelectorAll('.demo-download')).forEach(function(btn){ 219 btn.addEventListener('click', function(){ 220 openModal({ url: btn.getAttribute('data-download-url') || btn.getAttribute('href'), soundId: btn.getAttribute('data-sound-id') || '', title: btn.getAttribute('data-sound-title') || '' }); 221 }); 222 }); 223 224 applyFilters(); 225 } 226 if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initDemoSounds); 227 else initDemoSounds(); 228})();
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.