| <!DOCTYPE html><html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| <title>JioSaavn Mini Player • HF Space</title> |
| <meta name="description" content="A sleek web music player powered by the JioSaavn Unofficial API. Search, queue, play, shuffle, and view lyrics in one page." /> |
| <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Ccircle cx='128' cy='128' r='120' fill='%2300e5ff'/%3E%3Cpath d='M88 76v104a40 40 0 1 0 24-37.3V84h56V68H88z' fill='white'/%3E%3C/svg%3E" /> |
| <style> |
| :root{ |
| --bg: #0b0e14; |
| --panel: #111625; |
| --panel-2: #0f1421; |
| --text: #eef3fb; |
| --muted: #a9b4c7; |
| --brand: #00e5ff; |
| --brand-2: #6ce7ff; |
| --accent: #8a7dff; |
| --danger: #ff4976; |
| --good: #4ade80; |
| --warning: #f59e0b; |
| --shadow: 0 10px 30px rgba(0,0,0,.35); |
| --radius-xl: 18px; |
| --radius-lg: 14px; |
| --radius-md: 10px; |
| } |
| *{box-sizing:border-box} |
| html,body{height:100%} |
| body{ |
| margin:0; font: 15px/1.45 system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; |
| color:var(--text); background: radial-gradient(1200px 800px at 100% -100%, #1a1e2a 10%, transparent 60%), |
| radial-gradient(900px 700px at -10% -100%, #091427 20%, transparent 70%), |
| linear-gradient(180deg, #0b0e14, #0b0e14); |
| display:flex; flex-direction:column; min-height:100%; |
| } |
| a{color:var(--brand)} a:hover{opacity:.9} |
| header{ |
| position:sticky; top:0; z-index:40; backdrop-filter: blur(10px); |
| background: linear-gradient(180deg, rgba(17,22,37,.85), rgba(17,22,37,.65)); |
| border-bottom:1px solid rgba(255,255,255,.06); |
| } |
| .wrap{max-width:1200px; margin:0 auto; padding:16px;} |
| .topbar{display:flex; gap:12px; align-items:center; justify-content:space-between} |
| .brand{display:flex; align-items:center; gap:10px; font-weight:800; letter-spacing:.2px} |
| .brand .logo{width:36px; height:36px; border-radius:50%; box-shadow: var(--shadow); background: linear-gradient(120deg, var(--brand), var(--accent)); display:grid; place-items:center} |
| .brand .logo svg{filter:drop-shadow(0 6px 16px rgba(0,229,255,.35))}.search{flex:1; display:flex; gap:10px; align-items:center} |
| .search input{ |
| flex:1; border:none; outline:none; padding:14px 14px 14px 44px; color:var(--text); |
| background: linear-gradient(180deg, #0f1421, #0b0f1a); border:1px solid rgba(255,255,255,.07); |
| border-radius: var(--radius-lg); box-shadow: inset 0 0 0 1px rgba(255,255,255,.02); |
| } |
| .search .field{position:relative; flex:1} |
| .search .field svg{position:absolute; left:12px; top:50%; transform:translateY(-50%); opacity:.7} |
| .btn{background: linear-gradient(180deg, var(--brand), var(--brand-2)); color:#00212a; border:none; padding:12px 16px; border-radius: var(--radius-lg); font-weight:700; box-shadow:0 8px 24px rgba(108,231,255,.25); cursor:pointer} |
| .btn:active{transform:translateY(1px)} |
| .btn.alt{background:linear-gradient(180deg, #21283a, #171d2c); color:var(--text); border:1px solid rgba(255,255,255,.06)} |
| |
| .content{display:grid; grid-template-columns: 320px 1fr; gap:16px; align-items:start; padding-bottom:140px} |
| @media (max-width: 1020px){ .content{ grid-template-columns: 1fr; } } |
| |
| .panel{background: linear-gradient(180deg, var(--panel), var(--panel-2)); border:1px solid rgba(255,255,255,.06); border-radius: var(--radius-xl); box-shadow: var(--shadow)} |
| |
| |
| .queue{position:sticky; top:92px; max-height:calc(100dvh - 140px); overflow:auto} |
| .queue h3{margin:0; padding:14px 16px; font-size:14px; letter-spacing:.4px; text-transform:uppercase; opacity:.8} |
| .q-actions{display:flex; gap:8px; padding:0 12px 8px; flex-wrap:wrap} |
| .q-list{display:flex; flex-direction:column; gap:8px; padding:0 8px 12px} |
| .q-item{display:grid; grid-template-columns: 44px 1fr auto; gap:10px; align-items:center; padding:8px; border-radius:12px; cursor:pointer; border:1px solid transparent} |
| .q-item:hover{background:rgba(255,255,255,.04); border-color: rgba(255,255,255,.06)} |
| .q-item.active{background:linear-gradient(180deg, rgba(108,231,255,.12), rgba(138,125,255,.1)); border-color:rgba(108,231,255,.35)} |
| .thumb{width:44px; height:44px; border-radius:10px; overflow:hidden} |
| .thumb img{width:100%; height:100%; object-fit:cover} |
| .meta{min-width:0} |
| .title{font-weight:700; white-space:nowrap; overflow:hidden; text-overflow:ellipsis} |
| .artists{font-size:12px; color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis} |
| .pill{font-size:11px; padding:3px 8px; border-radius:999px; border:1px solid rgba(255,255,255,.12); color:var(--muted)} |
| |
| |
| .results{display:grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap:14px} |
| .card{position:relative; padding:12px; border-radius:18px; border:1px solid rgba(255,255,255,.06); background:linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,.01)); box-shadow: var(--shadow)} |
| .cover{position:relative; border-radius:14px; overflow:hidden; aspect-ratio:1/1} |
| .cover img{width:100%; height:100%; object-fit:cover; display:block} |
| .badge{position:absolute; right:8px; top:8px; font-size:11px; padding:4px 8px; border-radius:999px; background:rgba(0,229,255,.12); border:1px solid rgba(0,229,255,.3); color:var(--brand)} |
| .card .info{margin-top:10px} |
| .card .title{font-weight:800} |
| .actions{display:flex; gap:8px; margin-top:10px} |
| .icon-btn{display:inline-grid; place-items:center; border:1px solid rgba(255,255,255,.08); background:linear-gradient(180deg, #1b2234, #13192a); border-radius:12px; padding:8px; cursor:pointer} |
| .icon-btn:hover{border-color:rgba(255,255,255,.2)} |
| |
| |
| .now-playing{padding:16px} |
| .np-wrap{display:grid; grid-template-columns: 180px 1fr; gap:18px} |
| @media (max-width:720px){ .np-wrap{ grid-template-columns: 1fr; } } |
| .np-art{border-radius:16px; overflow:hidden; aspect-ratio:1/1; background:linear-gradient(180deg, #1b2234, #101624)} |
| .np-art img{width:100%; height:100%; object-fit:cover} |
| .np-meta .np-title{font-size:20px; font-weight:900} |
| .np-meta .np-artist{color:var(--muted)} |
| .np-ctrls{display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:10px} |
| .stack{display:flex; flex-direction:column; gap:10px} |
| .range{appearance:none; width:100%; height:6px; background:#0e1320; border-radius:999px; outline:none; border:1px solid rgba(255,255,255,.08)} |
| .range::-webkit-slider-thumb{appearance:none; width:14px; height:14px; border-radius:50%; background:var(--brand); box-shadow:0 0 0 6px rgba(0,229,255,.15)} |
| .time{display:flex; justify-content:space-between; font-size:12px; color:var(--muted)} |
| |
| |
| .lyrics{margin-top:8px; padding:12px; border-radius:12px; background:linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,.02)); border:1px solid rgba(255,255,255,.06); max-height:220px; overflow:auto; white-space:pre-wrap} |
| |
| |
| .bar{position:fixed; left:0; right:0; bottom:0; z-index:50; background:linear-gradient(180deg, rgba(15,20,33,.85), rgba(15,20,33,.95)); border-top:1px solid rgba(255,255,255,.07); backdrop-filter:blur(10px)} |
| .bar .wrap{display:grid; grid-template-columns: 1fr auto 1fr; align-items:center; gap:12px} |
| .bar .mini{display:flex; gap:10px; align-items:center; min-width:0} |
| .bar .mini .thumb{width:54px; height:54px; border-radius:12px} |
| .bar .mini .title{font-weight:800} |
| .bar .mini .artists{font-size:12px} |
| .center-ctrls{display:flex; align-items:center; justify-content:center; gap:8px} |
| .volume{display:flex; align-items:center; gap:8px; justify-content:flex-end} |
| |
| footer{padding:12px; text-align:center; color:var(--muted); font-size:12px} |
| |
| </style> |
| </head> |
| <body> |
| <header> |
| <div class="wrap topbar"> |
| <div class="brand"> |
| <div class="logo" aria-hidden="true"> |
| <svg width="22" height="22" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="128" cy="128" r="120" fill="#00E5FF"/><path d="M88 76v104a40 40 0 1 0 24-37.3V84h56V68H88z" fill="#fff"/></svg> |
| </div> |
| <div>HF • JioSaavn Mini</div> |
| </div> |
| <div class="search"> |
| <div class="field"> |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M21 21l-3.9-3.9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><circle cx="10" cy="10" r="6" stroke="currentColor" stroke-width="2"/></svg> |
| <input id="q" placeholder="Search songs, e.g. 'sanam'" value="sanam"/> |
| </div> |
| <button class="btn" id="go">Search</button> |
| <button class="btn alt" id="shuffleAll">Shuffle Results</button> |
| </div> |
| </div> |
| </header> <main class="wrap content"> |
| <aside class="panel queue" aria-label="Queue"> |
| <h3>Queue</h3> |
| <div class="q-actions"> |
| <button class="btn alt" id="clearQueue">Clear</button> |
| <button class="btn alt" id="saveQueue">Save</button> |
| <button class="btn alt" id="loadQueue">Load</button> |
| </div> |
| <div id="queue" class="q-list"></div> |
| </aside><section> |
| <div class="panel now-playing"> |
| <div class="np-wrap"> |
| <div class="np-art" id="npArt"><img alt="Cover" id="npImg" src=""/></div> |
| <div class="np-meta"> |
| <div class="np-title" id="npTitle">Nothing playing</div> |
| <div class="np-artist" id="npArtist">—</div> |
| <div class="stack"> |
| <input id="seek" class="range" type="range" min="0" max="100" value="0"/> |
| <div class="time"><span id="cur">0:00</span><span id="dur">0:00</span></div> |
| <div class="np-ctrls"> |
| <button class="icon-btn" id="prev" title="Prev (P)">⏮️</button> |
| <button class="icon-btn" id="play" title="Play/Pause (Space)">▶️</button> |
| <button class="icon-btn" id="next" title="Next (N)">⏭️</button> |
| <button class="icon-btn" id="repeat" title="Repeat All / One / Off">🔁</button> |
| <button class="icon-btn" id="shuffle" title="Toggle Shuffle (S)">🔀</button> |
| <div class="volume"> |
| <span>🔊</span> |
| <input id="vol" class="range" type="range" min="0" max="1" step="0.01" value="0.9" style="width:140px"/> |
| </div> |
| <button class="icon-btn" id="toggleLyrics" title="Toggle lyrics (L)">🎤</button> |
| <a class="icon-btn" id="openLink" href="#" target="_blank" rel="noopener" title="Open on JioSaavn">🔗</a> |
| </div> |
| <div class="lyrics" id="lyrics" hidden></div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="panel" style="padding:16px; margin-top:16px"> |
| <h3 style="margin:0 0 10px 0; opacity:.8; text-transform:uppercase; letter-spacing:.4px">Results</h3> |
| <div id="results" class="results"></div> |
| </div> |
| </section> |
|
|
| </main> <div class="bar"> |
| <div class="wrap"> |
| <div class="mini"> |
| <div class="thumb"><img id="barImg" alt=""></div> |
| <div> |
| <div class="title" id="barTitle">—</div> |
| <div class="artists" id="barArtists">—</div> |
| </div> |
| </div> |
| <div class="center-ctrls"> |
| <button class="icon-btn" id="bPrev">⏮️</button> |
| <button class="icon-btn" id="bPlay">▶️</button> |
| <button class="icon-btn" id="bNext">⏭️</button> |
| </div> |
| <div class="volume"> |
| <span>🔊</span> |
| <input id="bVol" class="range" type="range" min="0" max="1" step="0.01" value="0.9" style="width:200px"/> |
| </div> |
| </div> |
| </div> <footer>Unofficial demo. Streams are provided by JioSaavn CDN via the public API. Use for testing only.</footer><audio id="audio"></audio> |
|
|
| <script> |
| const API = 'https://jio-saavn-api-eta.vercel.app'; |
| |
| |
| const state = { |
| results: [], |
| queue: [], |
| index: -1, |
| repeat: 'all', |
| shuffle: false, |
| volume: parseFloat(localStorage.getItem('volume') || '0.9'), |
| }; |
| |
| |
| const $ = sel => document.querySelector(sel); |
| const $$ = sel => Array.from(document.querySelectorAll(sel)); |
| const fmtTime = s => { |
| if (isNaN(s) || !isFinite(s)) return '0:00'; |
| s = Math.max(0, Math.floor(s)); |
| const m = Math.floor(s/60); const sec = (s%60).toString().padStart(2,'0'); |
| return `${m}:${sec}`; |
| }; |
| |
| const pick = (o, keys) => keys.reduce((a,k)=>{ if (o && o[k] != null) a[k]=o[k]; return a; }, {}); |
| |
| |
| function mapTrack(it){ |
| const url = it.media_url || it.media_preview_url || it.vlink || ''; |
| const t = { |
| id: it.id || crypto.randomUUID(), |
| title: it.song || 'Unknown', |
| artists: it.primary_artists || it.singers || '', |
| image: it.image || '', |
| album: it.album || '', |
| year: it.year || '', |
| duration: Number(it.duration || 0), |
| url, |
| preview: it.media_preview_url || '', |
| vlink: it.vlink || '', |
| perma_url: it.perma_url ? (it.perma_url.startsWith('http')? it.perma_url : 'https://www.jiosaavn.com'+it.perma_url) : '#', |
| disabled: String(it.disabled||'false') === 'true', |
| rights: it.rights || null, |
| lyrics: it.lyrics || null, |
| }; |
| return t; |
| } |
| |
| |
| function renderResults(){ |
| const root = $('#results'); |
| root.innerHTML = ''; |
| if (!state.results.length){ root.innerHTML = '<div style="opacity:.7">No results. Try another search.</div>'; return; } |
| for(const t of state.results){ |
| const div = document.createElement('div'); |
| div.className = 'card'; |
| div.innerHTML = ` |
| <div class=\"cover\"><img src=\"${t.image}\" alt=\"${t.title}\">${t.disabled?'\n <span class=\"badge\" title=\"Might require pro on Saavn\">PRO</span>':''} |
| </div> |
| <div class=\"info\"> |
| <div class=\"title\">${t.title}</div> |
| <div class=\"artists\">${t.artists || '—'}</div> |
| </div> |
| <div class=\"actions\"> |
| <button class=\"icon-btn\" title=\"Play now\">▶️</button> |
| <button class=\"icon-btn\" title=\"Add to queue\">➕</button> |
| </div>`; |
| const [playBtn, addBtn] = div.querySelectorAll('.icon-btn'); |
| playBtn.onclick = () => { enqueue([t], true); }; |
| addBtn.onclick = () => { enqueue([t], false); }; |
| root.appendChild(div); |
| } |
| } |
| |
| function renderQueue(){ |
| const root = $('#queue'); root.innerHTML = ''; |
| state.queue.forEach((t, i)=>{ |
| const div = document.createElement('div'); |
| div.className = 'q-item' + (i===state.index? ' active':''); |
| div.innerHTML = ` |
| <div class=\"thumb\"><img src=\"${t.image}\" alt=\"${t.title}\"></div> |
| <div class=\"meta\"><div class=\"title\">${t.title}</div><div class=\"artists\">${t.artists || ''}</div></div> |
| <div style=\"display:flex; gap:6px\"> |
| <span class=\"pill\">${t.year || ''}</span> |
| <button class=\"icon-btn\" title=\"Remove\">✖️</button> |
| </div>`; |
| div.onclick = (e)=>{ if (!(e.target instanceof HTMLButtonElement)) playAt(i); }; |
| div.querySelector('button').onclick = (e)=>{ e.stopPropagation(); removeAt(i); }; |
| root.appendChild(div); |
| }); |
| } |
| |
| function renderNow(){ |
| const t = state.queue[state.index]; |
| const has = !!t; |
| $('#npImg').src = has? t.image : ''; |
| $('#barImg').src = has? t.image : ''; |
| $('#npTitle').textContent = has? t.title : 'Nothing playing'; |
| $('#barTitle').textContent = has? t.title : '—'; |
| $('#npArtist').textContent = has? t.artists : '—'; |
| $('#barArtists').textContent = has? t.artists : '—'; |
| $('#openLink').href = has? t.perma_url : '#'; |
| $('#lyrics').textContent = (has && t.lyrics) ? t.lyrics : (has? 'No lyrics found for this track.' : ''); |
| } |
| |
| |
| const audio = $('#audio'); |
| audio.preload = 'metadata'; |
| |
| function setVolume(v){ |
| state.volume = v; |
| audio.volume = v; |
| $('#vol').value = v; $('#bVol').value = v; |
| localStorage.setItem('volume', String(v)); |
| } |
| |
| async function playAt(i){ |
| if (i < 0 || i >= state.queue.length) return; |
| state.index = i; renderQueue(); renderNow(); |
| const t = state.queue[i]; |
| const src = t.url || t.preview || t.vlink; |
| if(!src){ alert('No playable URL for this track.'); return; } |
| audio.src = src; |
| try{ await audio.play(); togglePlayButtons(true);}catch(e){ console.warn(e); togglePlayButtons(false); } |
| updateTitles('▶'); |
| } |
| |
| function togglePlayButtons(isPlaying){ |
| $('#play').textContent = isPlaying? '⏸️':'▶️'; |
| $('#bPlay').textContent = isPlaying? '⏸️':'▶️'; |
| } |
| |
| function next(){ |
| if (!state.queue.length) return; |
| if (state.shuffle){ |
| const n = Math.floor(Math.random()*state.queue.length); |
| playAt(n); return; |
| } |
| const last = state.index === state.queue.length-1; |
| if (last){ |
| if (state.repeat === 'all') playAt(0); |
| else togglePlayButtons(false); |
| } else playAt(state.index+1); |
| } |
| function prev(){ if (audio.currentTime > 3) { audio.currentTime = 0; } else playAt(Math.max(0, state.index-1)); } |
| |
| audio.addEventListener('timeupdate', ()=>{ |
| $('#seek').value = (audio.currentTime / (audio.duration||1)) * 100; |
| $('#cur').textContent = fmtTime(audio.currentTime); |
| $('#dur').textContent = fmtTime(audio.duration); |
| }); |
| audio.addEventListener('ended', ()=>{ |
| if (state.repeat === 'one') { playAt(state.index); return; } |
| next(); |
| }); |
| audio.addEventListener('play', ()=> togglePlayButtons(true)); |
| audio.addEventListener('pause', ()=> togglePlayButtons(false)); |
| |
| |
| async function search(q){ |
| updateTitles('⏳'); |
| try{ |
| const res = await fetch(`${API}/song/?query=${encodeURIComponent(q)}&lyrics=true`); |
| const data = await res.json(); |
| state.results = Array.isArray(data) ? data.map(mapTrack) : []; |
| }catch(e){ console.error(e); state.results = []; } |
| renderResults(); updateTitles(); |
| } |
| |
| function enqueue(items, playNow=false){ |
| const before = state.queue.length; |
| for(const it of items){ state.queue.push(it); } |
| persist(); |
| renderQueue(); |
| if (playNow) playAt(before); |
| } |
| |
| function removeAt(i){ state.queue.splice(i,1); if (i <= state.index) state.index = Math.max(0, state.index-1); persist(); renderQueue(); } |
| |
| function persist(){ |
| const tiny = state.queue.map(t=>pick(t,['id','title','artists','image','album','year','duration','url','preview','vlink','perma_url','lyrics'])); |
| localStorage.setItem('queue', JSON.stringify({queue: tiny, index: state.index})); |
| } |
| |
| function restore(){ |
| try{ |
| const saved = JSON.parse(localStorage.getItem('queue')||'null'); |
| if (saved && Array.isArray(saved.queue)){ |
| state.queue = saved.queue; state.index = saved.index ?? -1; |
| } |
| }catch(e){} |
| } |
| |
| function updateTitles(prefix=''){ |
| document.title = `${prefix? prefix+' ':''}${state.queue[state.index]?.title || 'JioSaavn Mini Player • HF'}`; |
| } |
| |
| |
| $('#go').onclick = ()=> search($('#q').value.trim() || 'sanam'); |
| $('#shuffleAll').onclick = ()=>{ |
| if (!state.results.length) return; |
| const shuffled=[...state.results].sort(()=>Math.random()-.5); |
| enqueue(shuffled, true); |
| }; |
| |
| $('#clearQueue').onclick = ()=>{ state.queue=[]; state.index=-1; persist(); renderQueue(); renderNow(); }; |
| $('#saveQueue').onclick = ()=>{ persist(); alert('Queue saved locally.'); }; |
| $('#loadQueue').onclick = ()=>{ restore(); renderQueue(); if(state.index>=0) renderNow(); }; |
| |
| $('#play').onclick = ()=>{ if (audio.paused) audio.play(); else audio.pause(); }; |
| $('#bPlay').onclick = ()=> $('#play').onclick(); |
| $('#next').onclick = next; $('#bNext').onclick = next; |
| $('#prev').onclick = prev; $('#bPrev').onclick = prev; |
| |
| $('#vol').oninput = e=> setVolume(parseFloat(e.target.value)); |
| $('#bVol').oninput = e=> setVolume(parseFloat(e.target.value)); |
| |
| $('#seek').oninput = e=>{ const p = parseFloat(e.target.value)/100; audio.currentTime = p * (audio.duration||0); }; |
| |
| $('#shuffle').onclick = ()=>{ state.shuffle = !state.shuffle; $('#shuffle').style.filter = state.shuffle? 'drop-shadow(0 0 8px rgba(108,231,255,.8))':''; }; |
| $('#repeat').onclick = ()=>{ |
| state.repeat = state.repeat==='all'?'one': state.repeat==='one'?'off':'all'; |
| $('#repeat').textContent = state.repeat==='one'?'🔂': state.repeat==='off'?'🔁❌':'🔁'; |
| }; |
| |
| $('#toggleLyrics').onclick = ()=>{ const el=$('#lyrics'); el.hidden=!el.hidden; }; |
| |
| document.addEventListener('keydown', (e)=>{ |
| if (['INPUT','TEXTAREA'].includes(e.target.tagName)) return; |
| if (e.code==='Space'){ e.preventDefault(); $('#play').onclick(); } |
| if (e.key==='n' || e.key==='N') next(); |
| if (e.key==='p' || e.key==='P') prev(); |
| if (e.key==='s' || e.key==='S') $('#shuffle').onclick(); |
| if (e.key==='l' || e.key==='L') $('#toggleLyrics').onclick(); |
| if (e.key==='ArrowLeft') audio.currentTime = Math.max(0, audio.currentTime-5); |
| if (e.key==='ArrowRight') audio.currentTime = Math.min(audio.duration||0, audio.currentTime+5); |
| if (e.key==='ArrowUp') setVolume(Math.min(1, state.volume+0.05)); |
| if (e.key==='ArrowDown') setVolume(Math.max(0, state.volume-0.05)); |
| }); |
| |
| |
| restore(); |
| setVolume(state.volume); |
| renderQueue(); |
| if (state.index>=0 && state.queue[state.index]) { renderNow(); } |
| |
| search($('#q').value); |
| </script></body> |
| </html> |
| |
| |