=0?\\\"good\\\":\\\"bad\\\";pnlp.textContent=(inv?p/inv*100:0).toFixed(2)+\\\"%\\\";day.textContent=(daily>=0?\\\"+\\\":\\\"\\\")+money(daily,2)+\\\" за 24ч\\\";updated.textContent=state.market.updated_at?new Date(state.market.updated_at*1000).toLocaleTimeString(\\\"ru-RU\\\"):\\\"нет данных\\\";marketStatus.textContent=Object.keys(m).length===2?\\\"Рынок обновлён\\\":\\\"Нет данных\\\";marketStatus.className=\\\"status \\\"+(Object.keys(m).length===2?\\\"good\\\":\\\"bad\\\");bindInputs();draw();renderMoves();renderAlerts();aCoin.innerHTML=ids.map(id=>\\\"
\\\").join(\\\"\\\")}function bindInputs(){document.querySelectorAll(\\\".qty,.avg\\\").forEach(x=>x.onchange=async()=>{let id=x.dataset.id;let q=document.querySelector(\\\".qty[data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\"]\\\").value;let a=document.querySelector(\\\".avg[data-id=\\\\\\\"\\\"+id+\\\"\\\\\\\"]\\\").value;let avg=currency===\\\"RUB\\\"?Number(a)/fx():Number(a);await fetch(\\\"/api/holding\\\",{method:\\\"POST\\\",headers:{\\\"Content-T\"},{\"offset\":12971,\"pattern\":\"fetch\\\\s*\\\\(\",\"text\":\"k={price_above:\\\"цена ≥\\\",price_below:\\\"цена ≤\\\",cap_above:\\\"капитализация ≥\\\",cap_below:\\\"капитализация ≤\\\",pump:\\\"памп ≥ +\\\",dump:\\\"падение ≥ −\\\"}[a.kind];return c+\\\": \\\"+k+\\\" \\\"+a.threshold+(a.kind===\\\"pump\\\"||a.kind===\\\"dump\\\"?\\\"% за \\\"+Math.round(a.window_seconds/60)+\\\" мин\\\":\\\" USD\\\")}function renderAlerts(){alertList.innerHTML=state.alerts.length?state.alerts.map(a=>\\\"
\\\"+label(a)+\\\"\\\"+a.email+\\\"
\\\").join(\\\"\\\"):\\\"
Оповещений пока нет\\\";document.querySelectorAll(\\\".del\\\").forEach(x=>x.onclick=async()=>{await fetch(\\\"/api/alert/\\\"+x.dataset.id,{method:\\\"DELETE\\\"});load()})}aKind.onchange=()=>aWindow.classList.toggle(\\\"hidden\\\",!([\\\"pump\\\",\\\"dump\\\"].includes(aKind.value)));addAlert.onclick=async()=>{let dynamic=[\\\"pump\\\",\\\"dump\\\"].includes(aKind.value);let r=await fetch(\\\"/api/alert\\\",{method:\\\"POST\\\",headers:{\\\"Content-Type\\\":\\\"application/json\\\"},body:JSON.stringify({coin_id:aCoin.value,kind:aKind.value,threshold:Number(aThreshold.value),window_seconds:dynamic?Number(aWindow.value):0,email:aEmail.value})});if(!r.ok){alert(\\\"Проверьте параметры\\\")};aThreshold.value=\\\"\\\";load()};document.querySelectorAll(\\\".currency-btn\\\").forEach(x=>x.onclick=()=>{currency=x.dataset.currency;local\"},{\"offset\":16886,\"pattern\":\"fetch\\\\s*\\\\(\",\"text\":\"lue||\\\"\\\").trim() }; statusEl.className=\\\"muted full\\\"; statusEl.textContent=\\\"\\\"; if(!payload.coin_id){statusEl.className=\\\"bad full\\\";statusEl.textContent=\\\"Не выбрана монета\\\";return;} if(!Number.isFinite(payload.threshold)||payload.threshold<=0){statusEl.className=\\\"bad full\\\";statusEl.textContent=\\\"Укажи положительный порог\\\";return;} if(!payload.email||!payload.email.includes(\\\"@\\\")){statusEl.className=\\\"bad full\\\";statusEl.textContent=\\\"Укажи корректный email\\\";return;} button.disabled=true; const oldText=button.textContent; button.textContent=\\\"Создание…\\\"; try{ const beforeResponse=await fetch(\\\"/api/state\\\",{credentials:\\\"same-origin\\\",headers:{\\\"Accept\\\":\\\"application/json\\\"}}); if(beforeResponse.status===401){location=\\\"/login\\\";return;} if(!beforeResponse.ok){throw new Error(\\\"предварительная проверка HTTP \\\"+beforeResponse.status);} const before=await beforeResponse.json(); const beforeIds=new Set((before.alerts||[]).map(a=>Number(a.id))); const response=await fetch(\\\"/api/alert\\\",{ method:\\\"POST\\\", credentials:\\\"same-origin\\\", headers:{\\\"Content-Type\\\":\\\"application/json\\\",\\\"Accept\\\":\\\"application/json\\\"}, body:JSON.stringify(payload) }); const raw=await re\"},{\"offset\":21685,\"pattern\":\"fetch\\\\s*\\\\(\",\"text\":\"data,alert))); left.append(make(\\\"div\\\",\\\"muted\\\",alert.email||\\\"\\\")); const holding=(data.holdings&&data.holdings[alert.coin_id])||{}; const quantity=Number(holding.quantity||0); const isPrice=[\\\"price_above\\\",\\\"price_below\\\"].includes(alert.kind); const sale=make(\\\"div\\\",\\\"sale\\\",isPrice?alertMoney(data,quantity*Number(alert.threshold||0)):\\\"—\\\"); const button=make(\\\"button\\\",\\\"btn ghost del\\\",\\\"Удалить\\\"); button.type=\\\"button\\\"; button.dataset.alertDeleteV4=String(alert.id); button.onclick=async event=>{ event.preventDefault(); button.disabled=true; try{ const response=await fetch(\\\"/api/alert/\\\"+encodeURIComponent(alert.id),{method:\\\"DELETE\\\",credentials:\\\"same-origin\\\",cache:\\\"no-store\\\"}); if(response.status===401){location=\\\"/login\\\";return;} if(!response.ok)throw new Error(\\\"HTTP \\\"+response.status); await refresh(); }catch(error){ button.disabled=false; button.textContent=\\\"Ошибка удаления\\\"; } }; row.append(left,sale,button); box.append(row); }); }; const refresh=async()=>{ if(loading)return; loading=true; const box=document.getElementById(\\\"alertList\\\"); try{ const response=await fetch(\\\"/api/state\\\",{credentials:\\\"sa\"},{\"offset\":23740,\"pattern\":\"TradingView\",\"text\":\"}); observer.observe(box,{childList:true,subtree:true}); } const add=document.getElementById(\\\"addAlert\\\"); if(add){ add.addEventListener(\\\"click\\\",()=>{ setTimeout(refresh,400); setTimeout(refresh,1200); }); } refresh(); setInterval(refresh,15000); }; if(document.readyState===\\\"loading\\\")document.addEventListener(\\\"DOMContentLoaded\\\",install,{once:true});else install(); const redrawForCurrency=()=>{if(lastState)draw(lastState);}; document.querySelectorAll(\\\".currency-btn\\\").forEach(button=>button.addEventListener(\\\"click\\\",()=>setTimeout(redrawForCurrency,0))); })();