(function(){constr=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(constsofdocument.querySelectorAll('link[rel="modulepreload"]'))o(s);newMutationObserver(s=>{for(constcofs)if(c.type==="childList")for(constuofc.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&o(u)}).observe(document,{childList:!0,subtree:!0});functiona(s){constc={};returns.integrity&&(c.integrity=s.integrity),s.referrerPolicy&&(c.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?c.credentials="include":s.crossOrigin==="anonymous"?c.credentials="omit":c.credentials="same-origin",c}functiono(s){if(s.ep)return;s.ep=!0;constc=a(s);fetch(s.href,c)}})();constQe=newWorker(newURL("/assets/db-worker-DFFhVJkA.js",import.meta.url),{type:"module"});letpr=0,Ae=null;constet=newMap;functionOn(){returnAe||(Ae=newPromise((e,r)=>{Qe.onmessage=o=>{consts=o.data;if(s.type==="ready")return;constc=et.get(s.id);c&&(et.delete(s.id),s.type=="success"?c.resolve(s.data):c.reject(newError(s.error??s.message??"Неизвестная ошибка Worker")))},Qe.onerror=o=>{console.error("[SQLite Worker] Ошибка:",o),r(newError("Ошибка Worker"))};consta=++pr;et.set(a,{resolve:e,reject:r}),Qe.postMessage({type:"start",id:a})}),Ae)}functionhr(e,r){returnnewPromise((a,o)=>{if(!Qe){o(newError("Worker не создан"));return}consts=++pr;et.set(s,{resolve:a,reject:o}),Qe.postMessage({type:e,id:s,...r})})}asyncfunctionie(e,r){returnawaithr("query",{sql:e,params:r??[]})}asyncfunctionwr(e,r){awaithr("command",{sql:e,params:r??[]})}functiongr(e){returnnewDate(e*1e3)}functionJn(){returnMath.floor(Date.now()/1e3)}functionKn(e){constr=gr(e),a=String(r.getDate()).padStart(2,"0"),o=String(r.getMonth()+1).padStart(2,"0");return`${a}.${o}.${r.getFullYear()}`}functionDn(e){constr=gr(e),a=String(r.getHours()).padStart(2,"0"),o=String(r.getMinutes()).padStart(2,"0");return`${Kn(e)}${a}:${o}`}functionXn(e){if(e==null)return[];constr=String(e).trim();returnr?r.split(/[,\s;]+/).map(a=>a.trim()).filter(a=>a.length>0):[]}functionzn(e){constr=newMap;if(e==null)returnr;consta=String(e);if(!a)returnr;consto=a.split("");for(lets=0;s+1<o.length;s+=2){constc=Number(o[s]),u=o[s+1]??"";!Number.isFinite(c)||!Number.isInteger(c)||u&&r.set(c,u)}returnr}functionmr(e){constr=Number(e.id??0),a=String(e.key??""),o=String(e.value??""),s=e.tags==null?"":String(e.tags),c=Number(e.group_id??1);return{id:r,key:a,value:o,tags:s,tagsList:Xn(s),more:zn(e.more),group_id:Number.isFinite(c)?c:1}}functionvr(e){constr=[],a=[],o=(e.search??"").trim();if(o){r.push("(key LIKE ? ESCAPE '\\' OR value LIKE ? ESCAPE '\\' OR tags LIKE ? ESCAPE '\\')");consts=`%${o.replace(/\\/g,"\\\\").replace(/%/g,"\\%").replace(/_/g,"\\_")}%`;a.push(s,s,s)}returne.groupId!=null&&(r.push("group_id = ?"),a.push(e.groupId)),{clause:r.length>0?`WHERE ${r.join(" AND ")}`:"",params:a}}asyncfunctionBn(e={}){const{clause:r,params:a}=vr(e),s=(awaitie(`SELECT COUNT(*) as cnt FROM words ${r}`,a))[0]?.cnt;returntypeofs=="number"?s:Number(s??0)}asyncfunctiontr(e={}){const{clause:r,params:a}=vr(e),o=Math.max(1,Math.min(e.limit??50,500)),s=Math.max(0,e.offset??0);return(awaitie(`SELECT id, key, value, tags, more, group_id FROM words ${r} ORDER BY id LIMIT ? OFFSET ?`,[...a,o,s])).map(mr)}asyncfunctionVn(){return(awaitie("SELECT id, name FROM word_groups ORDER BY id")).map(r=>({id:Number(r.id),name:String(r.name??"")}))}asyncfunctionYn(){return(awaitie("SELECT id, name, forward, backward, filter FROM decks ORDER BY id")).map(r=>({id:Number(r.id),name:String(r.name??""),forward:String(r.forward??""),backward:String(r.backward??""),filter:String(r.filter??"")}))}asyncfunctionZn(e,r){returnawaitwr("UPDATE decks SET forward = ?, backward = ?, filter = ? WHERE id = ?",[r.forward,r.backward,r.filter,e]),nt(e)}asyncfunctionnt(e){consta=(awaitie("SELECT id, name, forward, backward, filter FROM decks WHERE id = ? LIMIT 1",[e]))[0];returna?{id:Number(a.id),name:String(a.name??""),forward:String(a.forward?
`;consta=e.querySelector("#home-db-banner"),o=e.querySelector("#home-db-text"),s=e.querySelector("#home-db-go"),c=e.querySelector("#words-search"),u=e.querySelector("#words-group"),h=e.querySelector("#words-count"),v=e.querySelector("#words-status"),p=e.querySelector("#words-list"),y=e.querySelector("#words-more");functionC(b,S){a.hidden=!1,o.textContent=b,s.hidden=!1}functionk(){a.hidden=!0}s.addEventListener("click",()=>{document.querySelector('.drawer-btn[data-page="sync"]')?.click()});letE=!1,$=newMap,w=0,U=[],I=null,O=0,H=!1,z=0,R=0;functionx(){constb=u.value;if(b==="")return{search:c.value.trim(),groupId:null};constS=Number(b);return{search:c.value.trim(),groupId:Number.isFinite(S)?S:null}}functionF(){y.hidden=!(E&&U.length<w),y.hidden||(y.textContent=`Показать ещё (${w-U.length} осталось)`)}functionQ(){p.innerHTML=U.map(b=>or(b,$,b.id===I)).join(""),h.textContent=w>0?`Показано ${U.length} из ${w}`:"",F()}functionq(b){if(I===b){b!=null&&X(b),I=null;return}I!=null&&X(I),I=b,b!=null&&j(b)}functionX(b){constS=p.querySelector(`.word-row-wrap[data-word-id="${b}"]`);if(!S)return;constT=S.querySelector(".word-row"),P=S.querySelector(".word-extra");T&&(T.classList.remove("open"),T.setAttribute("aria-expanded","false")),P&&(P.hidden=!0,P.innerHTML="")}functionj(b){constS=U.find(he=>he.id===b),T=p.querySelector(`.word-row-wrap[data-word-id="${b}"]`);if(!S||!T)return;constP=T.querySelector(".word-row"),J=T.querySelector(".word-extra");P&&(P.classList.add("open"),P.setAttribute("aria-expanded","true")),J&&(J.hidden=!1,J.innerHTML=Er(S,$))}functionV(b){v.textContent=b,v.hidden=b===""}asyncfunctionB(){if(!E||H)return;H=!0;constb=++z,S=x();y.disabled=!0,U.length===0&&V("Загрузка слов…");try{const[T,P]=awaitPromise.all([Bn({search:S.search,groupId:S.groupId??void0}),tr({search:S.search,groupId:S.groupId??void0,limit:ir,offset:0})]);if(b!==z)return;w=T,U=P,O=P.length,I!=null&&!U.some(J=>J.id===I)&&(I=null),Q(),V(P.length===0?"По этому запросу слов нет.":""),P.length===0&&w===0&&(h.textContent="Слов в базе пока нет.")}catch(T){if(b!==z)return;V(`Не удалось загрузить слова: ${TinstanceofError?T.message:String(T)}`)}finally{b===z&&(H=!1,y.disabled=!1,F())}}asyncfunctionL(){if(!E||H||U.length>=w)return;H=!0;constb=++z,S=x();y.disabled=!0,y.textContent="Загрузка…";try{constT=awaittr({search:S.search,groupId:S.groupId??void0,limit:ir,offset:O});if(b!==z)return;U=U.concat(T),O+=T.length,p.insertAdjacentHTML("beforeend",T.map(P=>or(P,$,P.id===I)).join("")),h.textContent=`Показано ${U.length} из ${w}`,F()}catch(T){if(b!==z)return;V(`Не удалось догрузить: ${TinstanceofError?T.message:String(T)}`)}finally{b===z&&(H=!1,y.disabled=!1,F())}}c.addEventListener("input",()=>{window.clearTimeout(R),R=window.setTimeout(()=>{B()},300)}),c.addEventListener("keydown",b=>{b.key==="Enter"&&(window.clearTimeout(R),B())}),u.addEventListener("change",()=>{B()}),y.addEventListener("click",()=>{L()}),p.addEventListener("click",b=>{constS=b.target.closest(".word-row");if(!S||S.disabled)return;constT=Number(S.dataset.wordId);Number.isFinite(T)&&q(I===T?null:T)}),(async()=>{if(!awaitOe()){C("Локальной базы нет — скачай сохранение."),V("Нет локальной базы — скачай сохранение на странице «Синхронизация»."),h.textContent="";return}try{constS=awaitaa();if(!$t(S)){C("Файл в OPFS не похож на SQLite — перекачай сохранение.",!0),V("Файл в OPFS не похож на SQLite — перекачай сохранение.");return}k();try{const[P,J]=awaitPromise.all([Vn(),yr()]),he=P;$=newMap(J.map(ue=>[ue.id,ue])),u.innerHTML='<option value="">Все группы</option>'+he.map(ue=>`<option value="${ue.id}">${Ee(ue.name)}</option>`).join(""),E=!0,awaitB(),u.options[0].textContent=`Все группы (${w})`}catch(P){V(`БД есть, но слова не про
`;consta=e.querySelector("#decks-count"),o=e.querySelector("#decks-status"),s=e.querySelector("#decks-list");functionc(u){o.textContent=u,o.hidden=u===""}s.addEventListener("click",u=>{consth=u.target.closest("[data-act]");if(!h)return;constv=Number(h.dataset.deckId);Number.isFinite(v)&&(h.dataset.act==="review"?r.goReview(v):r.goDeck(v))}),(async()=>{if(!awaitOe()){c("Нет локальной базы — скачай сохранение на странице «Синхронизация»."),a.textContent="";return}c("Загрузка колод…");try{const[h,v]=awaitPromise.all([Yn(),Gn()]);if(h.length===0){a.textContent="Колоды пока отсутствуют.",c(""),s.innerHTML="";return}a.textContent=`Всего: ${h.length}`,c(""),s.innerHTML=h.map(p=>Pa(p,v.get(p.id)??0)).join("")}catch(h){c(`Не удалось загрузить колоды: ${hinstanceofError?h.message:String(h)}`)}})()}functionQa(e){returne?Ia(e):'<span class="empty-val">—</span>'}functionIa(e){returne.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}functionbt(e){returne.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}functionOa(e){constr=e%10,a=e%100;returnr===1&&a!==11?"слово":r>=2&&r<=4&&(a<12||a>14)?"слова":"слов"}functionJa(e,r,a){e.innerHTML=`
`;consto=e.querySelector("#deck-back"),s=e.querySelector("#deck-title"),c=e.querySelector("#deck-status"),u=e.querySelector("#deck-body");o.addEventListener("click",()=>a.goDecks()),(async()=>{if(!awaitOe()){s.textContent="Колода недоступна",c.textContent="Нет локальной базы — скачай сохранение на странице «Синхронизация».";return}letv=null;try{v=awaitnt(r)}catch(x){s.textContent="Колода недоступна",c.textContent=`Не удалось загрузить колоду: ${xinstanceofError?x.message:String(x)}`;return}if(!v){s.textContent="Колода не найдена",c.textContent=`Колоды с id=${r} нет в локальной базе. Возможно, база обновилась — вернись к списку.`;return}constp=v;s.textContent=p.name||`Колода #${p.id}`,c.hidden=!0;lety=null;try{y=awaitjn(p.id)}catch(x){c.hidden=!1,c.textContent=`Данные колоды загружены, а количество слов — нет: ${xinstanceofError?x.message:String(x)}`}u.innerHTML=`
`;consto=e.querySelector("#hist-back"),s=e.querySelector("#hist-title"),c=e.querySelector("#hist-count"),u=e.querySelector("#hist-status"),h=e.querySelector("#hist-list"),v=e.querySelector("#hist-more");o.addEventListener("click",()=>a.goDeck(r));functionp(w){u.textContent=w,u.hidden=w===""}lety=0,C=0,k=!1;functionE(){constw=y-C;v.hidden=w<=0,w>0&&(v.textContent=`Показать ещё (${w} осталось)`)}asyncfunction$(){if(!(k||C>=y)){k=!0,v.disabled=!0,v.textContent="Загрузка…";try{constw=awaitra(r,{limit:Ka,offset:C});C+=w.length,h.insertAdjacentHTML("beforeend",w.map(Xa).join("")),c.textContent=`Показано ${C} из ${y}`}catch(w){p(`Не удалось загрузить историю: ${winstanceofError?w.message:String(w)}`)}finally{k=!1,v.disabled=!1,E()}}}v.addEventListener("click",()=>{$()}),(async()=>{if(!awaitOe()){s.textContent="История недоступна",p("Нет локальной базы — скачай сохранение на странице «Синхронизация».");return}try{constU=awaitnt(r);if(!U){s.textContent="Колода не найдена",p(`Колоды с id=${r} нет в локальной базе.`);return}if(s.textContent=U.name||`Колода #${U.id}`,y=awaitta(U.id),y===0){c.textContent="Ответов пока нет — начни повторение.",p("");return}p(""),await$()}catch(U){s.textContent="История недоступна",p(`Не удалось загрузить историю: ${UinstanceofError?U.message:String(U)}`)}})()}functionBa(e){returne.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}constVa=[{value:0,label:"Не получилось",cls:"grade-fail"},{value:1,label:"Тяжело",cls:"grade-hard"},{value:2,label:"Хорошо",cls:"grade-good"},{value:3,label:"Легко",cls:"grade-easy"}];functionYa(e,r,a){e.innerHTML=`
`,h.querySelector("#review-card").addEventListener("click",()=>{w||(w=!0,z())}),h.querySelectorAll(".review-grade").forEach(T=>{T.addEventListener("click",()=>{constP=Number(T.dataset.grade);R(P,j)})})}asyncfunctionR(Q,q){if(O)return;O=!0;constX=p[$],j=U??1,V=[...h.querySelectorAll(".review-grade")];for(constBofV)B.disabled=!0;try{awaitea({deckId:r,wordId:X,answer:Q,before:j,after:j})}catch(B){v(`Ответ не записался: ${BinstanceofError?B.message:String(B)}${q}`),O=!1;for(constLofV)L.disabled=!1;return}$+=1,w=!1,U=null,$>=p.length?x():awaitz()}functionx(){c.textContent=`Готово: ${p.length} из ${p.length}`,v(""),h.innerHTML=`
`,h.querySelector("#review-again").addEventListener("click",()=>{F()})}asyncfunctionF(){if(v("Перемешиваю…"),h.innerHTML="",c.textContent="",p=awaitar(r),$=0,w=!1,p.length===0){v("В колоде нет слов для повторения.");return}y=awaitrr(p),z()}(async()=>{if(!awaitOe()){s.textContent="Повторение недоступно",v("Нет локальной базы — скачай сохранение на странице «Синхронизация».");return}try{const[q,X]=awaitPromise.all([nt(r),yr()]);if(!q){s.textContent="Колода не найдена",v(`Колоды с id=${r} нет в локальной базе. Вернись к списку.`);return}if(C=X,k=q.forward,E=q.backward,s.textContent=q.name||`Колода #${q.id}`,p=awaitar(q.id),p.length===0){c.textContent="",v("В колоде нет слов для повторения.");return}y=awaitrr(p),$=0,w=!1,awaitz()}catch(q){s.textContent="Повторение недоступно",v(`Не удалось загрузить карточки: ${qinstanceofError?q.message:String(q)}`)}})()}constlr=24,Za=/^[A-Za-z0-9]{24}$/,Cr="japlearn_sync_token";functionur(e){returne.trim().replace(/\s+/g,"")}functionPe(e){returnZa.test(e)}functionGa(){try{returnlocalStorage.getItem(Cr)??""}catch{return""}}functiondr(e){try{localStorage.setItem(Cr,e)}catch{}}const$r="https://learning.micialware.ru";functionja(e,r=$r){return`${r.replace(/\/+$/,"")}/download/${encodeURIComponent(e)}`}asyncfunctionWa(e,r,a=$r){consto=ja(e,a);lets;try{s=awaitfetch(o,{method:"GET",...r})}catch(u){thrownewError(`Нет связи с сервером: ${uinstanceofError?u.message:String(u)}`)}if(!s.ok)throws.status===404?newError("Сохранение не найдено (404). Проверь токен."):newError(`Сервер ответил ${s.status}${s.statusText}`);constc=awaits.arrayBuffer();if(c.byteLength===0)thrownewError("Сервер вернул пустой файл");returnnewUint8Array(c)}functionAa(e){e.innerHTML=`
`;constr=e.querySelector("#token-input"),a=e.querySelector("#token-error"),o=e.querySelector("#token-counter"),s=e.querySelector("#btn-download"),c=e.querySelector("#status");functionu(k){c.textContent=k,console.log(`[JapLearn] ${k}`)}functionh(){returnur(r.value)}functionv(k){s.disabled=k||!Pe(h()),r.disabled=k}functionp(){constk=h(),E=k.length;o.textContent=`${E} / ${lr}`;const$=Pe(k),w=E===0;r.classList.toggle("invalid",!w&&!$),r.setAttribute("aria-invalid",String(!w&&!$)),w?a.textContent="":E!==lr?a.textContent=`Нужно 24 символа, сейчас ${E}`:a.textContent=$?"":"Только латинские буквы и цифры",s.disabled=!$}asyncfunctiony(){constk=h();if(Pe(k)){dr(k),v(!0),u("Скачивание сохранения...");try{constE=awaitWa(k);u(`Скачано ${(E.length/1024).toFixed(1)} КБ, распаковка...`);const$=awaitHa(E);u(`Распаковано ${($.length/1024).toFixed(1)} КБ, сохранение в OPFS...`),awaitna($),u(`Готово: база ${($.length/1024).toFixed(1)} КБ сохранена локально (OPFS).`)}catch(E){u(`Ошибка скачивания: ${EinstanceofError?E.message:String(E)}`)}finally{v(!1),p()}}}r.addEventListener("input",()=>{p()}),r.addEventListener("change",()=>{constk=h();r.value=k,Pe(k)&&(dr(k),u("Токен сохранён локально. Можно скачивать.")),p()}),s.addEventListener("click",()=>{y()});constC=Ga();C&&(r.value=C),p(),Pe(ur(C))&&u("Токен восстановлен. Нажми «Скачать», чтобы получить базу.")}constes="modulepreload",ts=function(e){return"/"+e},fr={},rs=function(r,a,o){lets=Promise.resolve();if(a&&a.length>0){letv=function(p){returnPromise.all(p.map(y=>Promise.resolve(y).then(C=>({status:"fulfilled",value:C}),C=>({status:"rejected",reason:C}))))};document.getElementsByTagName("link");constu=document.querySelector("meta[property=csp-nonce]"),h=u?.nonce||u?.getAttribute("nonce");s=v(a.map(p=>{if(p=ts(p),pinfr)return;fr[p]=!0;consty=p.endsWith(".css"),C=y?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${p}"]${C}`))return;constk=document.createElement("link");if(k.rel=y?"stylesheet":es,y||(k.as="script"),k.crossOrigin="",k.href=p,h&&k.setAttribute("nonce",h),document.head.appendChild(k),y)returnnewPromise((E,$)=>{k.addEventListener("load",E),k.addEventListener("error",()=>$(newError(`Unable to preload CSS for ${p}`)))})}))}functionc(u){consth=newEvent("vite:preloadError",{cancelable:!0});if(h.payload=u,window.dispatchEvent(h),!h.defaultPrevented)throwu}returns.then(u=>{for(consthofu||[])h.status==="rejected"&&c(h.reason);returnr().catch(c)})};functionns(e={}){const{immediate:r=!1,onNeedReload:a,onNeedRefresh:o,onOfflineReady:s,onRegistered:c,onRegisteredSW:u,onRegisterError:h}=e;letv,p,y;constC=async(E=!0)=>{awaitp,y?.()};asyncfunctionk(){if("serviceWorker"innavigator){if(v=awaitrs(async()=>{const{Workbox:E}=awaitimport("./workbox-window.prod.es5-BBnX5xw4.js");return{Workbox:E}},[]).then(({Workbox:E})=>newE("/sw.js",{scope:"/",type:"classic"})).catch(E=>{h?.(E)}),!v)return;y=()=>{v?.messageSkipWaiting()};{letE=!1;const$=()=>{E=!0,v?.addEventListener("controlling",w=>{w.isUpdate&&(a?a():window.location.reload())}),o?.()};v.addEventListener("installed",w=>{typeofw.isUpdate>"u"?typeofw.isExternal<"u"&&w.isExternal?$():!E&&s?.():w.isUpdate||s?.()}),v.addEventListener("waiting",$)}v.register({immediate:r}).then(E=>{u?u("/sw.js",E):c?.(E)}).catch(E=>{h?.(E)})}}returnp=k(),C}constUr="japlearn_ios_install_hint_seen";functionas(){conste=navigator;returnwindow.matchMedia("(display-mode: standalone)").matches||e.standalone===!0}functionss(){conste=navigator.userAgent;return(/iPhone|iPad|iPod/.test(e)||navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1)&&/WebKit/.test(e)&&!/CriOS|FxiOS|EdgiOS|OPiOS/.test(e)}functionis(){try{returnlocalStorage.getItem(Ur)==="1"}catch{return!0}}functionos(){conste=document.getElementById("pwa-banner");if(!e)return;letr=null,a=null,o=!1;funct