@hkonda/loco-translate 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -361,6 +361,73 @@ flowchart TB
361
361
 
362
362
  ---
363
363
 
364
+ ## Performance Benchmarks (Latest)
365
+
366
+ The performance suite compares **Server Mode** and **File Mode** at identical node counts.
367
+
368
+ Run:
369
+
370
+ ```bash
371
+ npx playwright test tests/performance.spec.ts
372
+ ```
373
+
374
+ Configuration used for results below:
375
+
376
+ - `ITERATIONS=11` total per test
377
+ - first iteration is warm-up and discarded
378
+ - reported averages use `n=10` recorded samples
379
+
380
+ ### Collection (Loco.textnodes)
381
+
382
+ #### Server
383
+
384
+ | Nodes | Avg (ms) [n] | Min (ms) | Max (ms) |
385
+ |---|---:|---:|---:|
386
+ | 100 | 0.03 [n=10] | 0.0 | 0.1 |
387
+ | 500 | 0.00 [n=10] | 0.0 | 0.0 |
388
+ | 1000 | 0.05 [n=10] | 0.0 | 0.1 |
389
+ | 3000 | 0.03 [n=10] | 0.0 | 0.1 |
390
+ | 5000 | 0.09 [n=10] | 0.0 | 0.2 |
391
+
392
+ #### File
393
+
394
+ | Nodes | Avg (ms) [n] | Min (ms) | Max (ms) |
395
+ |---|---:|---:|---:|
396
+ | 100 | 0.01 [n=10] | 0.0 | 0.1 |
397
+ | 500 | 0.02 [n=10] | 0.0 | 0.1 |
398
+ | 1000 | 0.04 [n=10] | 0.0 | 0.2 |
399
+ | 3000 | 0.08 [n=10] | 0.0 | 0.1 |
400
+ | 5000 | 0.24 [n=10] | 0.0 | 1.6 |
401
+
402
+ ### Apply (Loco.apply)
403
+
404
+ #### Server
405
+
406
+ | Nodes | Avg (ms) [n] | Min (ms) | Max (ms) |
407
+ |---|---:|---:|---:|
408
+ | 100 | 7.28 [n=10] | 6.5 | 8.1 |
409
+ | 500 | 17.30 [n=10] | 16.4 | 19.0 |
410
+ | 1000 | 17.68 [n=10] | 11.0 | 23.6 |
411
+ | 3000 | 29.01 [n=10] | 19.1 | 33.6 |
412
+ | 5000 | 157.07 [n=10] | 52.3 | 1092.0 |
413
+
414
+ #### File
415
+
416
+ | Nodes | Avg (ms) [n] | Min (ms) | Max (ms) |
417
+ |---|---:|---:|---:|
418
+ | 100 | 1.15 [n=10] | 0.8 | 1.5 |
419
+ | 500 | 3.55 [n=10] | 1.7 | 5.5 |
420
+ | 1000 | 4.68 [n=10] | 2.0 | 7.7 |
421
+ | 3000 | 11.62 [n=10] | 9.4 | 15.7 |
422
+ | 5000 | 19.59 [n=10] | 16.4 | 25.4 |
423
+
424
+ Notes:
425
+
426
+ - Collection time is near-zero for both modes; it is not a bottleneck.
427
+ - Apply time now remains low in both modes; file mode is fastest across all tested node counts.
428
+
429
+ ---
430
+
364
431
  ## Publishing an NPM Update
365
432
 
366
433
  Use this checklist whenever you add new features or fixes and want to release a new version.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkonda/loco-translate",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Self-hosted translation manager — auto-discovers text, AI translations, Svelte dashboard, CDN client.",
5
5
  "type": "module",
6
6
  "bin": {
package/public/loco.js CHANGED
@@ -4,6 +4,7 @@ var Loco = function() {
4
4
  apiKey: null,
5
5
  apiBase: null,
6
6
  phrases: [],
7
+ currentLang: null,
7
8
  translations: {},
8
9
  observer: null,
9
10
  fileMode: false,
@@ -1120,11 +1121,13 @@ var Loco = function() {
1120
1121
  if (!yieldEvery) yieldEvery = 100;
1121
1122
  var applied = 0;
1122
1123
  var skipped = 0;
1124
+ var frameStart = performance.now();
1123
1125
  for (var i = 0; i < phrases.length; i += yieldEvery) {
1124
- if (i > 0) {
1126
+ if (i > 0 && performance.now() - frameStart > 16) {
1125
1127
  await new Promise(function(resolve) {
1126
- setTimeout(resolve, 0);
1128
+ requestAnimationFrame(resolve);
1127
1129
  });
1130
+ frameStart = performance.now();
1128
1131
  }
1129
1132
  var batch = phrases.slice(i, i + yieldEvery);
1130
1133
  var result = applyTranslations(batch, translations);
@@ -1960,10 +1963,25 @@ var Loco = function() {
1960
1963
  return languageNames[c] ? { code: c, name: languageNames[c] } : c;
1961
1964
  });
1962
1965
  }
1966
+ function isPhraseActive(p) {
1967
+ if (!p) return false;
1968
+ if (p.type === "text") {
1969
+ return !!(p.textNode && p.textNode.parentElement && p.textNode.isConnected);
1970
+ }
1971
+ return !!(p.element && p.element.isConnected);
1972
+ }
1973
+ function getActivePhrases() {
1974
+ if (!Array.isArray(state.phrases) || state.phrases.length === 0) return [];
1975
+ var active = state.phrases.filter(isPhraseActive);
1976
+ if (active.length !== state.phrases.length) {
1977
+ state.phrases = active;
1978
+ }
1979
+ return state.phrases;
1980
+ }
1963
1981
  var Loco2 = {
1964
1982
  // Version is injected by Vite at build time from versions.json.
1965
1983
  // In-browser: Loco.version → e.g. "1.0.8"
1966
- version: "1.0.9",
1984
+ version: "1.0.11",
1967
1985
  init: function(config) {
1968
1986
  if (!config) {
1969
1987
  console.warn("[loco] Loco.init() requires a config object");
@@ -2008,6 +2026,12 @@ var Loco = function() {
2008
2026
  }
2009
2027
  },
2010
2028
  apply: async function(langCode) {
2029
+ var _perf = typeof window !== "undefined" && window.__locoPerfMode;
2030
+ if (_perf) {
2031
+ performance.clearMarks();
2032
+ performance.clearMeasures();
2033
+ performance.mark("loco-apply-start");
2034
+ }
2011
2035
  if (!langCode) {
2012
2036
  console.warn('[loco] Loco.apply() requires a language code, e.g. Loco.apply("zh-Hans")');
2013
2037
  return;
@@ -2046,13 +2070,28 @@ var Loco = function() {
2046
2070
  untranslate(state.phrases);
2047
2071
  state.translations = normalizeTranslationMap(state.fileData.translations[langCode]);
2048
2072
  state.fileData.translations[langCode] = state.translations;
2049
- state.phrases = await collectPhrasesAsync(document.body);
2073
+ if (_perf) performance.mark("loco-collect-start");
2074
+ var activeFilePhrases = getActivePhrases();
2075
+ if (activeFilePhrases.length === 0) {
2076
+ state.phrases = await collectPhrasesAsync(document.body);
2077
+ }
2078
+ if (_perf) {
2079
+ performance.mark("loco-collect-end");
2080
+ performance.measure("loco-collection-time", "loco-collect-start", "loco-collect-end");
2081
+ }
2050
2082
  var allTransKeys = Object.keys(state.translations);
2051
2083
  buildFileVarKeyMap(state.phrases, allTransKeys);
2084
+ if (_perf) performance.mark("loco-dom-start");
2052
2085
  var result = await applyTranslationsAsync(state.phrases, state.translations);
2086
+ if (_perf) {
2087
+ performance.mark("loco-dom-end");
2088
+ performance.measure("loco-dom-write-time", "loco-dom-start", "loco-dom-end");
2089
+ performance.measure("loco-apply-total", "loco-apply-start", "loco-dom-end");
2090
+ }
2053
2091
  state.observer = watchAndTranslate(state.phrases.map(function(p) {
2054
2092
  return p.key + "\0" + (p.context || "");
2055
2093
  }), state.translations);
2094
+ state.currentLang = langCode;
2056
2095
  saveLang(langCode);
2057
2096
  console.log("[loco] (file) applied " + result.applied + " translation(s), " + result.skipped + " pending");
2058
2097
  return result;
@@ -2069,7 +2108,15 @@ var Loco = function() {
2069
2108
  try {
2070
2109
  var map = await fetchTranslations(langCode);
2071
2110
  state.translations = map || {};
2072
- state.phrases = await collectPhrasesAsync(document.body);
2111
+ if (_perf) performance.mark("loco-collect-start");
2112
+ var activeServerPhrases = getActivePhrases();
2113
+ if (activeServerPhrases.length === 0) {
2114
+ state.phrases = await collectPhrasesAsync(document.body);
2115
+ }
2116
+ if (_perf) {
2117
+ performance.mark("loco-collect-end");
2118
+ performance.measure("loco-collection-time", "loco-collect-start", "loco-collect-end");
2119
+ }
2073
2120
  buildFileVarKeyMap(state.phrases, Object.keys(state.translations));
2074
2121
  postKeys(state.phrases).then(function(result2) {
2075
2122
  if (result2 && result2.keyMap) {
@@ -2078,10 +2125,17 @@ var Loco = function() {
2078
2125
  }
2079
2126
  }).catch(function() {
2080
2127
  });
2128
+ if (_perf) performance.mark("loco-dom-start");
2081
2129
  var result = await applyTranslationsAsync(state.phrases, state.translations);
2130
+ if (_perf) {
2131
+ performance.mark("loco-dom-end");
2132
+ performance.measure("loco-dom-write-time", "loco-dom-start", "loco-dom-end");
2133
+ performance.measure("loco-apply-total", "loco-apply-start", "loco-dom-end");
2134
+ }
2082
2135
  state.observer = watchAndTranslate(state.phrases.map(function(p) {
2083
2136
  return p.key + "\0" + (p.context || "");
2084
2137
  }), state.translations);
2138
+ state.currentLang = langCode;
2085
2139
  saveLang(langCode);
2086
2140
  console.log("[loco] applied " + result.applied + " translation(s), " + result.skipped + " pending");
2087
2141
  return result;
@@ -2092,11 +2146,13 @@ var Loco = function() {
2092
2146
  restore: function() {
2093
2147
  untranslate(state.phrases);
2094
2148
  if (state.observer) state.observer.disconnect();
2149
+ state.currentLang = null;
2095
2150
  saveLang(null);
2096
2151
  },
2097
2152
  rescan: async function() {
2098
2153
  var hadTranslations = Object.keys(state.translations).length > 0;
2099
2154
  if (hadTranslations) untranslate(state.phrases);
2155
+ state.currentLang = null;
2100
2156
  state.phrases = await collectPhrasesAsync(document.body);
2101
2157
  if (!state.fileMode) {
2102
2158
  postKeys(state.phrases).catch(function() {
@@ -2238,6 +2294,7 @@ var Loco = function() {
2238
2294
  state.fileData.translations = {};
2239
2295
  state.fileData.timestamp = 0;
2240
2296
  }
2297
+ state.currentLang = null;
2241
2298
  state.loadedFromCache = false;
2242
2299
  saveLang(null);
2243
2300
  untranslate(state.phrases);
@@ -2350,6 +2407,7 @@ var Loco = function() {
2350
2407
  });
2351
2408
  if (anyUpdated) {
2352
2409
  state.loadedFromCache = false;
2410
+ state.currentLang = null;
2353
2411
  var currentLang = getSavedLang();
2354
2412
  if (currentLang) {
2355
2413
  if (state.observer) {
@@ -1,4 +1,4 @@
1
- var Loco=function(){"use strict";var i={apiKey:null,apiBase:null,phrases:[],translations:{},observer:null,fileMode:!1,fileData:null,fileReadyResolve:null,fileReady:null,fileUrl:null,fileUrls:[],scanStopped:!1,loadedFromCache:!1,screenshotsEnabled:!0,widgetPosition:null},we=["SCRIPT","STYLE","NOSCRIPT","IFRAME","CODE","SVG","AUDIO","VIDEO","LINK"],Le=["VAR","STRONG","EM","B","I","SPAN","A","ABBR","MARK","SMALL","SUB","SUP","U","S","TIME"],Se=["h1","h2","h3","h4","h5","h6","label","legend","caption","figcaption","nav","header","footer","main","section","article","form"],A=new Set(we),D=new Set(Le),he=[].concat(Se),pe=0,ge=!0,k=1e3,G=1e4,me="loco-lang";function U(){try{return localStorage.getItem(me)}catch{return null}}function J(e){try{e?localStorage.setItem(me,e):localStorage.removeItem(me)}catch{}}function Be(e){if(e.blockedTags){var r=e.blockedTags.disabled||[],t=e.blockedTags.custom||[];A=new Set(we.filter(function(n){return r.indexOf(n)<0})),t.forEach(function(n){A.add(n.toUpperCase())})}if(e.inlineTags){var r=e.inlineTags.disabled||[],t=e.inlineTags.custom||[];D=new Set(Le.filter(function(o){return r.indexOf(o)<0})),t.forEach(function(o){D.add(o.toUpperCase())})}if(e.domContextSelectors){var r=e.domContextSelectors.disabled||[],t=e.domContextSelectors.custom||[];he=Se.filter(function(o){return r.indexOf(o)<0}).concat(t)}e.screenshotsEnabled===!1&&(i.screenshotsEnabled=!1),typeof e.contextDepth=="number"&&(pe=e.contextDepth),e.domContextEnabled===!1&&(ge=!1)}function N(e){return e.trim().replace(/\s+/g," ")}function E(e){return!/[a-zA-Z\u00C0-\u024F\u0900-\u097F\u0600-\u06FF]/.test(e)}function Z(e){var r=e.replace(/\{\{(?:text|number|decimal|date):\d+\}\}/g,"");return!r.trim()||E(r)}function Ae(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function B(e){var t;if(!e||e.nodeType!==1)return!1;const r=(t=e.tagName)==null?void 0:t.toUpperCase();return A.has(r)||e.hasAttribute("notranslate")||e.hasAttribute("data-notranslate")||e.hasAttribute("data-loco-translated")||e.getAttribute("translate")==="no"||e.isContentEditable}function Fe(e){let r=e.parentElement;for(;r&&r!==document.body;){if(B(r))return!0;r=r.parentElement}return!1}const ke=new Set(["H1","H2","H3","H4","H5","H6"]),He=new Set(["SCRIPT","STYLE","NOSCRIPT","CODE"]);let ve=new WeakMap,P=new WeakMap,I=null;function Ce(){if(ge){I=new Set;var e=he.slice();try{var r=e.join(",");document.querySelectorAll(r).forEach(function(t){I.add(t)})}catch{e.forEach(function(n){try{document.querySelectorAll(n).forEach(function(a){I.add(a)})}catch{}})}I.forEach(function(t){P.has(t)||H(t)})}}function z(e){return I?I.has(e):ke.has(e.tagName)?!0:he.some(function(r){try{return e.matches(r)}catch{return!1}})}function je(e){let r="";for(const t of e.childNodes)if(t.nodeType===Node.TEXT_NODE){const n=t.textContent.trim();n&&(r+=(r?" ":"")+n)}return r}function Xe(e,r){for(var t="",n=document.createTreeWalker(e,NodeFilter.SHOW_ALL,{acceptNode:function(o){return o.nodeType===Node.ELEMENT_NODE?He.has(o.tagName)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_SKIP:NodeFilter.FILTER_ACCEPT}}),a;(a=n.nextNode())&&!(a.nodeType===Node.TEXT_NODE&&(t+=a.textContent,t.length>=r)););return t.trim()}function H(e){if(P.has(e))return P.get(e);let r=null;const t=je(e);if(t)return r=t.substring(0,60),P.set(e,r),r;if(ke.has(e.tagName)){const a=(e.textContent||"").trim().replace(/\s+/g," ");if(a)return r=a.substring(0,60),P.set(e,r),r}var n=Xe(e,200);if(n){const a=n.split(/\n/)[0].trim().replace(/\s+/g," ");a.length>=2&&!E(a)&&(r=a.substring(0,60))}return P.set(e,r),r}function L(e){if(!e||!ge)return"";if(ve.has(e))return ve.get(e);const r=[],t=new Set;let n=e,a=0;if(e&&e.tagName==="TD"){var o=e.closest("table");if(o){var s=o.querySelector("thead tr th:nth-child("+(e.cellIndex+1)+")");if(s){var h=H(s);h&&(t.add(h),r.push(h))}}}for(;n&&n!==document.body&&!(pe>0&&a>=pe);){a++;let d=n.previousElementSibling;for(;d;){var m=d.tagName.toUpperCase();if(A.has(m)){d=d.previousElementSibling;continue}if(m==="ARTICLE"||m==="MAIN"){d=d.previousElementSibling;continue}if(z(d)){const f=H(d);f&&!t.has(f)&&(t.add(f),r.unshift(f));break}let p=null;for(const f of d.children)if(z(f)){p=f;break}if(!p)for(const f of d.children){for(const l of f.children)if(z(l)){p=l;break}if(p)break}if(p){const f=H(p);f&&!t.has(f)&&(t.add(f),r.unshift(f));break}d=d.previousElementSibling}if(z(n)){const p=H(n);p&&!t.has(p)&&(t.add(p),r.unshift(p))}var c=n.tagName;if(c==="ARTICLE"||c==="MAIN")break;n=n.parentElement}const u=r.join(" > ");return ve.set(e,u),u}function Qe(e){return/^\d+$/.test(e)?"number":/^\d+\.\d+$/.test(e)?"decimal":"text"}function We(e,r){for(var t=r.split(/(\{\{(?:text|number|decimal|date):\d+\}\})/),n=[],a=e,o=0;o<t.length;o++){var s=t[o],h=s.match(/^\{\{(text|number|decimal|date):(\d+)\}\}$/);if(h)if(a.indexOf(s)===0)n.push({type:h[1],index:parseInt(h[2]),originalText:s}),a=a.substring(s.length);else{for(var m="",c=o+1;c<t.length;c++){var u=t[c];if(/^\{\{(?:text|number|decimal|date):\d+\}\}$/.test(u)){if(a.indexOf(u)>=0){m=u;break}}else if(u!==""){m=u;break}}var d;if(m==="")d=a,a="";else{var p=a.indexOf(m);p>=0?(d=a.substring(0,p),a=a.substring(p)):(d=a,a="")}n.push({type:h[1],index:parseInt(h[2]),originalText:d})}else a.indexOf(s)===0&&(a=a.substring(s.length))}return n}function Y(e,r){e.forEach(function(t){if(!(t.varSlots&&t.varSlots.length>0)){var n=r[t.key];n&&(t.varSlots=We(t.key,n),t.key=n)}})}function $e(e,r){for(var t=r.split(/\{\{(?:text|number|decimal|date):\d+\}\}/g),n=e,a=[],o=0;o<t.length;o++)if(o===0)n.indexOf(t[o])===0&&(n=n.substring(t[o].length));else if(t[o]===""&&o===t.length-1)a.push(n),n="";else{var s=n.indexOf(t[o]);s>=0&&(a.push(n.substring(0,s)),n=n.substring(s+t[o].length))}return a.length>0&&a.every(function(h){return/^[a-zA-Z\s]+$/.test(h.trim())})}function Ve(e){for(var r=0,t=/\{\{(text|number|decimal|date):\d+\}\}/g,n;(n=t.exec(e))!==null;)switch(n[1]){case"number":r+=3;break;case"decimal":r+=3;break;case"date":r+=2;break;default:r+=1;break}return r}function R(e,r){for(var t=[],n=0;n<r.length;n++){var a=r[n];if(!(a.indexOf("{{text:")<0&&a.indexOf("{{number:")<0&&a.indexOf("{{decimal:")<0&&a.indexOf("{{date:")<0)){for(var o=[],s=[],h=0,m=/\{\{(text|number|decimal|date):\d+\}\}/g,c;(c=m.exec(a))!==null;)o.push(a.substring(h,c.index)),s.push(c[1]),h=m.lastIndex;o.push(a.substring(h));for(var u="",d=0;d<o.length;d++)if(u+=o[d].replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),d<s.length)switch(s[d]){case"number":u+="\\d+";break;case"decimal":u+="\\d+\\.\\d+";break;default:u+=".+";break}t.push({pattern:a,regex:new RegExp("^"+u+"$"),specificity:Ve(a)})}}if(t.length!==0){t.sort(function(l,g){return g.specificity-l.specificity});for(var p={},f={},n=0;n<r.length;n++)f[r[n]]=!0;return e.forEach(function(l){if(!f[l.key]&&!(l.key.length>k)){for(var g=0;g<t.length;g++)if(t[g].regex.test(l.key)&&!$e(l.key,t[g].pattern)){p[l.key]=t[g].pattern;break}}}),Y(e,p),p}}function F(e){let r=!1,t=!1;for(const n of e.childNodes)if(n.nodeType===Node.TEXT_NODE&&n.textContent.trim()&&(r=!0),n.nodeType===Node.ELEMENT_NODE){const a=n.tagName.toUpperCase();if(a==="BR")return!1;(a==="VAR"||a==="TIME"&&n.hasAttribute("datetime")||D.has(a))&&(t=!0)}return r&&t}function ee(e){let r="";const t=[];for(const n of e.childNodes){if(n.nodeType===Node.TEXT_NODE){const a=N(n.textContent);a&&(r+=a);continue}if(n.nodeType===Node.ELEMENT_NODE){const a=n.tagName.toUpperCase();if(a==="TIME"&&n.hasAttribute("datetime")){const o=t.length;t.push({type:"date",index:o,raw:n.getAttribute("datetime"),display:n.textContent,node:n}),r+=`{{date:${o}}}`;continue}if(a==="VAR"||D.has(a)){const o=N(n.textContent);if(o){const s=t.length,h=Qe(o);t.push({type:h,index:s,tag:a,value:o,node:n}),r+=`{{${h}:${s}}}`}continue}}}return{key:N(r),slots:t}}var te=["title","placeholder","aria-label"];function Oe(e){const r=[],t=new Set;if(e.nodeType===Node.ELEMENT_NODE&&!B(e)&&F(e)){const{key:c,slots:u}=ee(e);if(c&&c.length<=k)if(Z(c))u.forEach(function(d){if(d.type==="text"&&d.value&&d.value.length>=2&&!E(d.value)){var p=L(d.node||e),f=d.value+"\0"+p;t.has(f)||(t.add(f),r.push({type:"text",key:d.value,slots:[],textNode:d.node?d.node.firstChild:null,element:d.node||e,context:p,original:d.value}))}});else{var n=L(e),a=c+"\0"+n;t.has(a)||t.add(a);const d=u.some(p=>p.type==="date")?"mixed-date":u.some(p=>p.type==="text")?"mixed-text":"mixed";r.push({type:d,key:c,slots:u,element:e,context:n,original:c,originalHTML:e.innerHTML})}}const o=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT,{acceptNode(c){var g,v,x;if(c.nodeType===Node.TEXT_NODE){const y=N(c.textContent);if(!y||y.length<2||y.length>k||E(y)||/\{\{(?:text|number|decimal|date):\d+\}\}/.test(y)||Fe(c))return NodeFilter.FILTER_SKIP;const b=(v=(g=c.parentElement)==null?void 0:g.tagName)==null?void 0:v.toUpperCase();if(A.has(b)||F(c.parentElement))return NodeFilter.FILTER_SKIP;for(var u=c.parentElement;u;){var d=(x=u.tagName)==null?void 0:x.toUpperCase();if((d==="VAR"||d==="TIME"||D.has(d))&&u.parentElement&&F(u.parentElement))return NodeFilter.FILTER_SKIP;u=u.parentElement}return NodeFilter.FILTER_ACCEPT}if(c.nodeType===Node.ELEMENT_NODE){const y=c.tagName.toUpperCase();if(A.has(y)||B(c))return NodeFilter.FILTER_REJECT;if(y==="INPUT"){var p=(c.getAttribute("type")||"").toLowerCase();if(p==="button"||p==="submit"||p==="reset"){var f=N(c.value);if(f&&f.length>=2&&!E(f))return NodeFilter.FILTER_ACCEPT}var l=te.some(function(b){var T=N(c.getAttribute(b)||"");return T&&T.length>=2&&!E(T)});return l?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}return F(c)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}return NodeFilter.FILTER_SKIP}});let s;for(;s=o.nextNode();){if(s.nodeType===Node.TEXT_NODE){const c=N(s.textContent);if(!c)continue;var n=L(s.parentElement),a=c+"\0"+n;t.has(a)||t.add(a),r.push({type:"text",key:c,slots:[],textNode:s,element:s.parentElement,context:n,original:c});continue}if(s.nodeType===Node.ELEMENT_NODE&&s.tagName.toUpperCase()==="INPUT"){var h=(s.getAttribute("type")||"").toLowerCase(),n=L(s);if(h==="button"||h==="submit"||h==="reset"){var m=N(s.value);if(m&&m.length>=2&&!E(m)){var a=m+"\0"+n;t.has(a)||t.add(a),r.push({type:"input-value",key:m,slots:[],element:s,context:n,original:m})}}te.forEach(function(u){var d=N(s.getAttribute(u)||"");if(!(!d||d.length<2||E(d))){var p=d+"\0"+n+"\0"+u;t.has(p)||(t.add(p),r.push({type:"input-attr",key:d,attr:u,slots:[],element:s,context:n,original:d}))}});continue}if(s.nodeType===Node.ELEMENT_NODE){const{key:c,slots:u}=ee(s);if(!c||c.length>k)continue;if(Z(c)){u.forEach(function(l){if(l.type==="text"&&l.value&&l.value.length>=2&&!E(l.value)){var g=L(l.node||s),v=l.value+"\0"+g;t.has(v)||(t.add(v),r.push({type:"text",key:l.value,slots:[],textNode:l.node?l.node.firstChild:null,element:l.node||s,context:g,original:l.value}))}});continue}var n=L(s),a=c+"\0"+n;t.has(a)||t.add(a);const f=u.some(l=>l.type==="date")?"mixed-date":u.some(l=>l.type==="text")?"mixed-text":"mixed";r.push({type:f,key:c,slots:u,element:s,context:n,original:c,originalHTML:s.innerHTML})}}return r}async function j(e,r){r||(r=100);const t=[],n=new Set;if(e.nodeType===Node.ELEMENT_NODE&&!B(e)&&F(e)){const{key:f,slots:l}=ee(e);if(f&&f.length<=k)if(Z(f))l.forEach(function(g){if(g.type==="text"&&g.value&&g.value.length>=2&&!E(g.value)){var v=L(g.node||e),x=g.value+"\0"+v;n.has(x)||(n.add(x),t.push({type:"text",key:g.value,slots:[],textNode:g.node?g.node.firstChild:null,element:g.node||e,context:v,original:g.value}))}});else{var a=L(e),o=f+"\0"+a;n.has(o)||n.add(o);const g=l.some(v=>v.type==="date")?"mixed-date":l.some(v=>v.type==="text")?"mixed-text":"mixed";t.push({type:g,key:f,slots:l,element:e,context:a,original:f,originalHTML:e.innerHTML})}}const s=[],h=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT,{acceptNode(f){var b,T,S;if(f.nodeType===Node.TEXT_NODE){const w=N(f.textContent);if(!w||w.length<2||w.length>k||E(w)||/\{\{(?:text|number|decimal|date):\d+\}\}/.test(w)||Fe(f))return NodeFilter.FILTER_SKIP;const K=(T=(b=f.parentElement)==null?void 0:b.tagName)==null?void 0:T.toUpperCase();if(A.has(K)||F(f.parentElement))return NodeFilter.FILTER_SKIP;for(var l=f.parentElement;l;){var g=(S=l.tagName)==null?void 0:S.toUpperCase();if((g==="VAR"||g==="TIME"||D.has(g))&&l.parentElement&&F(l.parentElement))return NodeFilter.FILTER_SKIP;l=l.parentElement}return NodeFilter.FILTER_ACCEPT}if(f.nodeType===Node.ELEMENT_NODE){const w=f.tagName.toUpperCase();if(A.has(w)||B(f))return NodeFilter.FILTER_REJECT;if(w==="INPUT"){var v=(f.getAttribute("type")||"").toLowerCase();if(v==="button"||v==="submit"||v==="reset"){var x=N(f.value);if(x&&x.length>=2&&!E(x))return NodeFilter.FILTER_ACCEPT}var y=te.some(function(K){var O=N(f.getAttribute(K)||"");return O&&O.length>=2&&!E(O)});return y?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}return F(f)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}return NodeFilter.FILTER_SKIP}});for(var m;m=h.nextNode();)s.push(m);for(var c=0;c<s.length;c++){c>0&&c%r===0&&await new Promise(function(f){setTimeout(f,0)});var u=s[c];if(u.nodeType===Node.TEXT_NODE){const f=N(u.textContent);if(!f)continue;var a=L(u.parentElement),o=f+"\0"+a;n.has(o)||n.add(o),t.push({type:"text",key:f,slots:[],textNode:u,element:u.parentElement,context:a,original:f});continue}if(u.nodeType===Node.ELEMENT_NODE&&u.tagName.toUpperCase()==="INPUT"){var d=(u.getAttribute("type")||"").toLowerCase(),a=L(u);if(d==="button"||d==="submit"||d==="reset"){var p=N(u.value);if(p&&p.length>=2&&!E(p)){var o=p+"\0"+a;n.has(o)||n.add(o),t.push({type:"input-value",key:p,slots:[],element:u,context:a,original:p})}}te.forEach(function(l){var g=N(u.getAttribute(l)||"");if(!(!g||g.length<2||E(g))){var v=g+"\0"+a+"\0"+l;n.has(v)||(n.add(v),t.push({type:"input-attr",key:g,attr:l,slots:[],element:u,context:a,original:g}))}});continue}if(u.nodeType===Node.ELEMENT_NODE){const{key:f,slots:l}=ee(u);if(!f||f.length>k)continue;if(Z(f)){l.forEach(function(y){if(y.type==="text"&&y.value&&y.value.length>=2&&!E(y.value)){var b=L(y.node||u),T=y.value+"\0"+b;n.has(T)||(n.add(T),t.push({type:"text",key:y.value,slots:[],textNode:y.node?y.node.firstChild:null,element:y.node||u,context:b,original:y.value}))}});continue}var a=L(u),o=f+"\0"+a;n.has(o)||n.add(o);const x=l.some(y=>y.type==="date")?"mixed-date":l.some(y=>y.type==="text")?"mixed-text":"mixed";t.push({type:x,key:f,slots:l,element:u,context:a,original:f,originalHTML:u.innerHTML})}}return t}function ne(e){if(i.scanStopped)return Promise.resolve({ok:!0,registered:0,keyMap:{}});var r=new Set,t=[];return e.forEach(function(n){var a=n.key+"\0"+(n.context||"");r.has(a)||n.varSlots&&n.varSlots.length>0&&/\{\{(?:text|number|decimal|date):\d+\}\}/.test(n.key)||(r.add(a),t.push({key:n.key,context:n.context||""}),n.slots&&n.slots.length>0&&n.slots.forEach(function(o){if(o.type==="text"&&o.value&&!E(o.value)){var s=o.value+"\0";r.has(s)||(r.add(s),t.push({key:o.value,context:""}))}}))}),fetch(i.apiBase+"/api/textnodes",{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":i.apiKey},body:JSON.stringify({keys:t,url:window.location.href})}).then(function(n){return n.json()})}function qe(e){var r=i.apiBase+"/api/translations?lang="+encodeURIComponent(e);return fetch(r,{headers:{"X-API-Key":i.apiKey}}).then(function(t){return t.json()}).then(function(t){var n={};return Array.isArray(t)?t.forEach(function(a){n[a.key+"\0"+(a.context||"")]=a.value,n.hasOwnProperty(a.key)||(n[a.key]=a.value)}):n=t,n})}function Ge(){if(!(i.fileMode||!i.screenshotsEnabled)){var e=document.createElement("script");e.src=i.apiBase+"/cdn/html2canvas.min.js",e.onload=function(){typeof html2canvas=="function"&&html2canvas(document.body,{scale:.35,logging:!1,useCORS:!0,allowTaint:!0,width:window.innerWidth,height:window.innerHeight,windowWidth:window.innerWidth,windowHeight:window.innerHeight}).then(function(r){var t=r.toDataURL("image/jpeg",.5),n=t.split(",")[1];!n||n.length>5e5||fetch(i.apiBase+"/api/screenshots",{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":i.apiKey},body:JSON.stringify({url:window.location.href,screenshot:n})}).catch(function(){})}).catch(function(){})},e.onerror=function(){},document.head.appendChild(e)}}function Je(e,r){return e.replace(/\{\{(text|number|decimal|date):(\d+)\}\}/g,(t,n,a)=>{const o=r[parseInt(a)];return o?n==="date"?o.display:o.value:t})}function De(e){if(!Array.isArray(e))return e;var r={};return e.forEach(function(t){r[t.key+"\0"+(t.context||"")]=t.value,r.hasOwnProperty(t.key)||(r[t.key]=t.value)}),r}function ye(e,r){let t=0,n=0;return e.forEach(a=>{const o=a.key+"\0"+(a.context||"");var s=r[o]||r[a.key];if(!s&&a.slots&&a.slots.length>0){var h=a.slots.some(function(d){if(d.type!=="text"||!d.value)return!1;var p=d.value+"\0"+(a.context||""),f=d.value+"\0";return r.hasOwnProperty(p)||r.hasOwnProperty(f)||r.hasOwnProperty(d.value)});h&&(s=a.key)}if(!s){n++;return}if(a.type==="input-value"&&a.element)try{a.element.value=s,a.element.setAttribute("data-loco-translated",""),t++;return}catch(d){console.warn("[translate] Could not write to input value",d),n++;return}if(a.type==="input-attr"&&a.element&&a.attr)try{a.element.setAttribute(a.attr,s),a.element.setAttribute("data-loco-translated",""),t++;return}catch(d){console.warn("[translate] Could not write to input attr",d),n++;return}if(a.type==="text"&&a.textNode)try{var m=s;a.varSlots&&a.varSlots.length>0&&(m=m.replace(/\{\{(text|number|decimal|date):(\d+)\}\}/g,function(d,p,f){var l=a.varSlots.find(function(g){return(g.type||"text")===p&&g.index===+f});return l||(l=a.varSlots[+f]),l?l.originalText:d})),m=m.replace(/\{\{(?:text|number|decimal|date):\d+\}\}/g,""),a.textNode.nodeValue=m,a.textNode.parentElement&&a.textNode.parentElement.setAttribute("data-loco-translated",""),t++;return}catch(d){console.warn("[translate] Could not write to text node",d),n++;return}if(a.element){try{const d=Je(s,a.slots);let p=s;var c={};a.slots.forEach(f=>{const l=`{{${f.type}:${f.index}}}`;if(f.type==="date"&&f.node)f.node.textContent=f.display,c[l]=f.node.outerHTML;else if(f.node){if(f.type==="text"&&f.value){var g=f.value+"\0"+(a.context||""),v=f.value+"\0",x=r[g]||r[v]||r[f.value];x&&(f.node.textContent=x)}c[l]=f.node.outerHTML}}),a.varSlots&&a.varSlots.length>0&&a.varSlots.forEach(function(f){var l="{{"+f.type+":"+f.index+"}}";c.hasOwnProperty(l)||(c[l]=Ae(f.originalText))});var u=p.split(/(\{\{(?:text|number|decimal|date):\d+\}\})/g);p=u.map(function(f){return c.hasOwnProperty(f)?c[f]:/^\{\{(?:text|number|decimal|date):\d+\}\}$/.test(f)?"":Ae(f)}).join(""),a.element.innerHTML=p,a.element.setAttribute("data-loco-translated",""),t++}catch(d){console.warn("[translate] Could not apply mixed translation",d),n++}return}n++}),{applied:t,skipped:n}}async function re(e,r,t){t||(t=100);for(var n=0,a=0,o=0;o<e.length;o+=t){o>0&&await new Promise(function(m){setTimeout(m,0)});var s=e.slice(o,o+t),h=ye(s,r);n+=h.applied,a+=h.skipped}return{applied:n,skipped:a}}function C(e){let r=0;e.forEach(t=>{try{t.type==="input-value"&&t.element?(t.element.value=t.original,t.element.removeAttribute("data-loco-translated"),r++):t.type==="input-attr"&&t.element&&t.attr?(t.element.setAttribute(t.attr,t.original),t.element.removeAttribute("data-loco-translated"),r++):t.type==="text"&&t.textNode&&(t.textNode.nodeValue=t.original,t.textNode.parentElement&&t.textNode.parentElement.removeAttribute("data-loco-translated"),r++)}catch(n){console.warn("[translate] Could not restore text node",n)}}),e.forEach(t=>{if(t.type!=="text")try{t.element&&t.originalHTML!==void 0&&(t.element.innerHTML=t.originalHTML,t.element.removeAttribute("data-loco-translated"),r++)}catch(n){console.warn("[translate] Could not restore node",n)}}),console.log("[loco] untranslated "+r+" phrase(s)")}function ae(e,r){const t=new Set(e),n=[];Object.values(r).forEach(function(l){if(l&&(t.add(l),/\{\{(text|number|decimal|date):\d+\}\}/.test(l))){var g=l.split(/\{\{(?:text|number|decimal|date):\d+\}\}/g),v=g.map(function(x){return x.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")});n.push(new RegExp("^"+v.join(".+")+"$"))}});function a(l){if(t.has(l))return!0;for(var g=0;g<n.length;g++)if(n[g].test(l))return!0;return!1}let o=[],s=null,h=null,m=new Set,c=null,u=!1;function d(){if(o.length!==0){var l=o.splice(0);i.fileMode||ne(l).then(function(g){g&&g.keyMap&&(Y(l,g.keyMap),u=!0,ye(l,r),c&&c.takeRecords(),u=!1)}).catch(function(){})}}function p(l){const g=Oe(l);g.forEach(v=>{var x=v.key+"\0"+(v.context||"");a(v.key)||t.has(x)||(t.add(x),t.add(v.key),o.push(v),i.phrases.push(v))}),R(g,Object.keys(r)),u=!0,ye(g,r),c&&c.takeRecords(),u=!1,i.fileMode||(clearTimeout(s),s=setTimeout(d,500))}function f(l){m.add(l),h||(h=requestAnimationFrame(function(){h=null;var g=Array.from(m);m.clear(),g.forEach(function(v){v.isConnected&&p(v)})}))}return c=new MutationObserver(l=>{if(!u){var g=[],v=new Set;for(const y of l)for(const b of y.addedNodes)if(b.nodeType===Node.ELEMENT_NODE)g.push(b);else if(b.nodeType===Node.TEXT_NODE){var x=b.parentElement;x&&x.isConnected&&v.add(x)}g.forEach(function(y){p(y)}),v.forEach(function(y){f(y)})}}),c.observe(document.body,{childList:!0,subtree:!0}),c}var Pe="loco",Ze=2;function X(){return new Promise(function(e,r){try{var t=indexedDB.open(Pe,Ze);t.onupgradeneeded=function(n){var a=n.target.result;a.objectStoreNames.contains("meta")&&n.oldVersion<2&&a.deleteObjectStore("meta"),a.objectStoreNames.contains("meta")||a.createObjectStore("meta",{keyPath:"key"}),a.objectStoreNames.contains("translations")||a.createObjectStore("translations",{keyPath:"lang"})},t.onsuccess=function(n){e(n.target.result)},t.onerror=function(){r(t.error)}}catch(n){r(n)}})}function ie(){return X().then(function(e){return new Promise(function(r,t){var n=e.transaction("meta","readonly"),a=n.objectStore("meta").get("registry");a.onsuccess=function(){e.close();var o=a.result;r(o?{languages:o.languages||[],languageNames:o.languageNames||{}}:null)},a.onerror=function(){e.close(),t(a.error)}})})}function Ie(e){var r="source:"+e;return X().then(function(t){return new Promise(function(n,a){var o=t.transaction("meta","readonly"),s=o.objectStore("meta").get(r);s.onsuccess=function(){t.close();var h=s.result;n(h?{url:h.url,timestamp:h.timestamp,storedAt:h.storedAt}:null)},s.onerror=function(){t.close(),a(s.error)}})})}function ze(e){return X().then(function(r){return new Promise(function(t,n){var a=r.transaction("translations","readonly"),o=a.objectStore("translations").get(e);o.onsuccess=function(){r.close();var s=o.result;t(s?s.data:null)},o.onerror=function(){r.close(),n(o.error)}})})}function Ye(){return X().then(function(e){return new Promise(function(r,t){var n=e.transaction(["meta","translations"],"readonly"),a=n.objectStore("meta"),o=a.get("registry");o.onsuccess=function(){var s=o.result;if(!s||!s.languages||s.languages.length===0){e.close(),r(null);return}var h=U(),m=h||s.languages[0],c=n.objectStore("translations").get(m);c.onsuccess=function(){e.close();var u=c.result;if(!u||!u.data){r(null);return}var d={};d[m]=u.data,r({languages:s.languages,languageNames:s.languageNames||{},translations:d})},c.onerror=function(){e.close(),t(c.error)}},o.onerror=function(){e.close(),t(o.error)}})})}function oe(e,r,t){var n=r.languages||[],a=r.languageNames||{},o=r.translations||{},s=r.timestamp||0;return X().then(function(h){return new Promise(function(m,c){var u=h.transaction(["meta","translations"],"readwrite"),d=u.objectStore("meta"),p=u.objectStore("translations"),f=d.get("registry");f.onsuccess=function(){var l=f.result,g,v;if(t&&l){var x=l.languages||[];g=x.slice();for(var y=0;y<n.length;y++)g.indexOf(n[y])===-1&&g.push(n[y]);v={};var b=l.languageNames||{},T;for(T in b)v[T]=b[T];for(T in a)v[T]=a[T]}else g=n,v=a;d.put({key:"registry",languages:g,languageNames:v}),d.put({key:"source:"+e,url:e,timestamp:s,storedAt:Date.now()});for(var S=0;S<n.length;S++)o[n[S]]&&(t&&Array.isArray(o[n[S]])?function(w){var K=p.get(w);K.onsuccess=function(){var O=K.result,Q=O&&Array.isArray(O.data)?O.data:null,W=o[w];if(Q){for(var Ke="\0",$={},de=[],V=0;V<Q.length;V++){var be=Q[V].key+Ke+(Q[V].context||"");$.hasOwnProperty(be)||de.push(be),$[be]=Q[V]}for(var q=0;q<W.length;q++){var Te=W[q].key+Ke+(W[q].context||"");$.hasOwnProperty(Te)||de.push(Te),$[Te]=W[q]}for(var Ue=[],Ne=0;Ne<de.length;Ne++)Ue.push($[de[Ne]]);p.put({lang:w,data:Ue})}else p.put({lang:w,data:W})}}(n[S]):p.put({lang:n[S],data:o[n[S]]}))},u.oncomplete=function(){h.close(),m()},u.onerror=function(){h.close(),c(u.error)}})})}function et(){return new Promise(function(e){var r=indexedDB.deleteDatabase(Pe);r.onsuccess=function(){e()},r.onerror=function(){e()},r.onblocked=function(){e()}})}var tt='<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" width="32" height="32"><defs><clipPath id="loco-wc"><circle cx="100" cy="100" r="90"/></clipPath></defs><circle cx="100" cy="100" r="90" fill="#1a1f2e"/><g clip-path="url(#loco-wc)"><g transform="translate(100,108) scale(0.72) translate(-334,-198)"><ellipse cx="310" cy="318" rx="10" ry="38" fill="#2ECC88" transform="rotate(-18 310 318)"/><ellipse cx="326" cy="326" rx="9" ry="42" fill="#34D99A" transform="rotate(-6 326 326)"/><ellipse cx="342" cy="328" rx="9" ry="42" fill="#7BC74A" transform="rotate(6 342 328)"/><ellipse cx="357" cy="320" rx="9" ry="36" fill="#F07040" transform="rotate(18 357 320)"/><ellipse cx="334" cy="240" rx="62" ry="80" fill="#2ECC88"/><ellipse cx="334" cy="252" rx="36" ry="52" fill="#D4F4B0"/><path d="M275 220 Q228 190 232 265 Q248 295 280 285 Q268 255 275 220Z" fill="#3A8FE0"/><path d="M277 225 Q238 208 240 262 Q252 285 278 277 Q268 252 277 225Z" fill="#6BB3FF" opacity="0.7"/><path d="M393 220 Q440 190 436 265 Q420 295 388 285 Q400 255 393 220Z" fill="#3A8FE0"/><path d="M391 225 Q430 208 428 262 Q416 285 390 277 Q400 252 391 225Z" fill="#6BB3FF" opacity="0.7"/><ellipse cx="334" cy="172" rx="38" ry="30" fill="#2ECC88"/><circle cx="334" cy="148" r="52" fill="#2ECC88"/><ellipse cx="334" cy="118" rx="30" ry="18" fill="#FFB833"/><circle cx="312" cy="138" r="14" fill="white"/><circle cx="315" cy="140" r="9" fill="#2C2C2A"/><circle cx="315" cy="140" r="4" fill="#04342C"/><circle cx="319" cy="136" r="3.5" fill="white"/><circle cx="356" cy="138" r="14" fill="white"/><circle cx="353" cy="140" r="9" fill="#2C2C2A"/><circle cx="353" cy="140" r="4" fill="#04342C"/><circle cx="357" cy="136" r="3.5" fill="white"/><path d="M326 155 Q334 144 342 155 Q342 170 334 173 Q326 170 326 155Z" fill="#E8A020"/><path d="M328 165 Q334 158 340 165 Q340 174 334 176 Q328 174 328 165Z" fill="#A06010"/><ellipse cx="302" cy="152" rx="12" ry="8" fill="#F07040" opacity="0.8"/><ellipse cx="366" cy="152" rx="12" ry="8" fill="#F07040" opacity="0.8"/><path d="M320 100 Q316 68 308 52" stroke="#FFB833" stroke-width="6" fill="none" stroke-linecap="round"/><path d="M334 97 Q334 64 334 46" stroke="#7BC74A" stroke-width="6" fill="none" stroke-linecap="round"/><path d="M348 100 Q352 68 360 52" stroke="#F07040" stroke-width="6" fill="none" stroke-linecap="round"/><circle cx="308" cy="50" r="8" fill="#FFB833"/><circle cx="334" cy="44" r="8" fill="#7BC74A"/><circle cx="360" cy="50" r="8" fill="#F07040"/><rect x="260" y="330" width="168" height="10" rx="5" fill="#8B5E20"/><path d="M310 330 L300 350 M310 330 L315 352 M310 330 L325 348" stroke="#A07030" stroke-width="4" fill="none" stroke-linecap="round"/><path d="M360 330 L350 350 M360 330 L365 352 M360 330 L375 348" stroke="#A07030" stroke-width="4" fill="none" stroke-linecap="round"/></g></g></svg>',xe=null;function se(e,r){xe=r;var t=document.getElementById("loco-lang-widget");t&&t.remove();var n={},a=e.map(function(l){return typeof l=="object"&&l.code?(l.name&&(n[l.code]=l.name),l.code):l});function o(l){return n[l]||l}var s=U()||null,h=document.createElement("div");h.id="loco-lang-widget",h.setAttribute("data-notranslate","");var m={"bottom-right":"bottom:20px;right:20px;","bottom-left":"bottom:20px;left:20px;","top-right":"top:20px;right:20px;","top-left":"top:20px;left:20px;"};h.style.cssText='position:fixed;z-index:2147483647;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-size:14px;'+(m[r]||m["bottom-right"]);var c=document.createElement("button");c.innerHTML=tt,c.style.cssText="width:52px;height:52px;border-radius:50%;border:none;background:#1a1f2e;color:#fff;font-size:22px;cursor:pointer;box-shadow:0 4px 14px rgba(0,0,0,0.25);display:flex;align-items:center;justify-content:center;transition:transform 0.2s,box-shadow 0.2s;overflow:hidden;padding:0;",c.onmouseenter=function(){c.style.transform="scale(1.1)",c.style.boxShadow="0 6px 20px rgba(0,0,0,0.35)"},c.onmouseleave=function(){c.style.transform="scale(1)",c.style.boxShadow="0 4px 14px rgba(0,0,0,0.25)"};var u=document.createElement("div");u.style.cssText="display:none;position:absolute;"+(r.indexOf("bottom")===0?"bottom:56px;":"top:56px;")+(r.indexOf("right")>=0?"right:0;":"left:0;")+"background:#fff;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,0.18);min-width:200px;max-height:320px;overflow-y:auto;padding:6px 0;";var d=document.createElement("div");d.textContent="Original",d.style.cssText="padding:10px 16px;cursor:pointer;color:#333;transition:background 0.15s;font-weight:600;border-bottom:1px solid #eee;",d.onmouseenter=function(){d.style.background="#f5f5f5"},d.onmouseleave=function(){d.style.background="transparent"},d.onclick=function(){s=null,window.Loco.restore(),f(),u.style.display="none"},u.appendChild(d);var p=[];a.forEach(function(l){var g=document.createElement("div");g.textContent=o(l),g.setAttribute("data-lang",l),g.style.cssText="padding:10px 16px;cursor:pointer;color:#333;transition:background 0.15s;",g.onmouseenter=function(){g.style.background="#f5f5f5"},g.onmouseleave=function(){g.style.background=s===l?"#e8f0fe":"transparent"},g.onclick=function(){s=l,window.Loco.apply(l),f(),u.style.display="none"},u.appendChild(g),p.push({el:g,code:l})});function f(){d.style.background=s===null?"#e8f0fe":"transparent",d.style.fontWeight=s===null?"600":"400",p.forEach(function(l){l.el.style.background=s===l.code?"#e8f0fe":"transparent",l.el.style.fontWeight=s===l.code?"600":"400"})}c.onclick=function(l){l.stopPropagation(),u.style.display=u.style.display==="none"?"block":"none"},document.addEventListener("click",function(l){h.contains(l.target)||(u.style.display="none")}),f(),h.appendChild(u),h.appendChild(c),document.body.appendChild(h)}function nt(e){xe&&(!e||e.length===0||se(e,xe))}var _={__proto__:1,constructor:1,prototype:1};function Re(e){if(!e||typeof e!="string")return!1;if(e.charAt(0)==="/"||e.charAt(0)===".")return!0;try{var r=new URL(e,window.location.origin);return r.protocol==="http:"||r.protocol==="https:"}catch{return!1}}function le(e){if(!e||typeof e!="object"||Array.isArray(e))return null;if(Array.isArray(e._raw)){for(var r={},t={},n=0;n<e._raw.length;n++){var a=e._raw[n];!a||typeof a.l!="string"||typeof a.k!="string"||typeof a.v!="string"||(r[a.l]||(r[a.l]=!0,t[a.l]=[]),t[a.l].push({key:a.k,context:typeof a.c=="string"?a.c:"",value:a.v}))}e={languages:Object.keys(r).sort(),languageNames:{},translations:t,timestamp:0}}var o={};if(!Array.isArray(e.languages))return null;o.languages=[];for(var s=0;s<e.languages.length;s++){if(typeof e.languages[s]!="string"||e.languages[s].length>20)return null;o.languages.push(e.languages[s])}if(o.timestamp=typeof e.timestamp=="number"&&isFinite(e.timestamp)?e.timestamp:0,o.languageNames={},e.languageNames&&typeof e.languageNames=="object"&&!Array.isArray(e.languageNames))for(var h in e.languageNames)!e.languageNames.hasOwnProperty(h)||_[h]||typeof e.languageNames[h]=="string"&&e.languageNames[h].length<=100&&(o.languageNames[h]=e.languageNames[h]);if(o.translations={},e.translations&&typeof e.translations=="object"&&!Array.isArray(e.translations)){for(var m in e.translations)if(!(!e.translations.hasOwnProperty(m)||_[m])&&o.languages.indexOf(m)!==-1){var c=e.translations[m];if(Array.isArray(c)){for(var u=[],d=0;d<c.length;d++){var p=c[d];!p||typeof p!="object"||typeof p.key!="string"||typeof p.value!="string"||p.key.length>G||p.value.length>G||_[p.key]||u.push({key:p.key,value:p.value,context:typeof p.context=="string"?p.context:""})}o.translations[m]=u}else if(typeof c=="object"){var f={};for(var l in c)!c.hasOwnProperty(l)||_[l]||typeof c[l]=="string"&&(l.length>G||c[l].length>G||(f[l]=c[l]));o.translations[m]=f}}}return o}async function _e(){try{var e=await fetch(i.apiBase+"/api/project/crawler-config",{headers:{"X-API-Key":i.apiKey}});e.ok&&Be(await e.json())}catch{}Ce(),i.phrases=await j(document.body),ne(i.phrases).then(function(t){t&&t.keyMap&&Y(i.phrases,t.keyMap)}).catch(function(t){console.warn("[loco] Failed to register keys:",t)}),!i.scanStopped&&i.screenshotsEnabled&&setTimeout(Ge,3e3),console.log("[loco] "+i.phrases.length+" text nodes discovered"),console.log(` Loco.textnodes() — list all discovered text nodes
1
+ var Loco=function(){"use strict";var o={apiKey:null,apiBase:null,phrases:[],currentLang:null,translations:{},observer:null,fileMode:!1,fileData:null,fileReadyResolve:null,fileReady:null,fileUrl:null,fileUrls:[],scanStopped:!1,loadedFromCache:!1,screenshotsEnabled:!0,widgetPosition:null},we=["SCRIPT","STYLE","NOSCRIPT","IFRAME","CODE","SVG","AUDIO","VIDEO","LINK"],Le=["VAR","STRONG","EM","B","I","SPAN","A","ABBR","MARK","SMALL","SUB","SUP","U","S","TIME"],Ae=["h1","h2","h3","h4","h5","h6","label","legend","caption","figcaption","nav","header","footer","main","section","article","form"],S=new Set(we),P=new Set(Le),he=[].concat(Ae),pe=0,me=!0,F=1e3,G=1e4,ge="loco-lang";function U(){try{return localStorage.getItem(ge)}catch{return null}}function J(e){try{e?localStorage.setItem(ge,e):localStorage.removeItem(ge)}catch{}}function He(e){if(e.blockedTags){var n=e.blockedTags.disabled||[],t=e.blockedTags.custom||[];S=new Set(we.filter(function(r){return n.indexOf(r)<0})),t.forEach(function(r){S.add(r.toUpperCase())})}if(e.inlineTags){var n=e.inlineTags.disabled||[],t=e.inlineTags.custom||[];P=new Set(Le.filter(function(i){return n.indexOf(i)<0})),t.forEach(function(i){P.add(i.toUpperCase())})}if(e.domContextSelectors){var n=e.domContextSelectors.disabled||[],t=e.domContextSelectors.custom||[];he=Ae.filter(function(i){return n.indexOf(i)<0}).concat(t)}e.screenshotsEnabled===!1&&(o.screenshotsEnabled=!1),typeof e.contextDepth=="number"&&(pe=e.contextDepth),e.domContextEnabled===!1&&(me=!1)}function T(e){return e.trim().replace(/\s+/g," ")}function E(e){return!/[a-zA-Z\u00C0-\u024F\u0900-\u097F\u0600-\u06FF]/.test(e)}function Z(e){var n=e.replace(/\{\{(?:text|number|decimal|date):\d+\}\}/g,"");return!n.trim()||E(n)}function Se(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function B(e){var t;if(!e||e.nodeType!==1)return!1;const n=(t=e.tagName)==null?void 0:t.toUpperCase();return S.has(n)||e.hasAttribute("notranslate")||e.hasAttribute("data-notranslate")||e.hasAttribute("data-loco-translated")||e.getAttribute("translate")==="no"||e.isContentEditable}function ke(e){let n=e.parentElement;for(;n&&n!==document.body;){if(B(n))return!0;n=n.parentElement}return!1}const Fe=new Set(["H1","H2","H3","H4","H5","H6"]),je=new Set(["SCRIPT","STYLE","NOSCRIPT","CODE"]);let ve=new WeakMap,D=new WeakMap,I=null;function Ce(){if(me){I=new Set;var e=he.slice();try{var n=e.join(",");document.querySelectorAll(n).forEach(function(t){I.add(t)})}catch{e.forEach(function(r){try{document.querySelectorAll(r).forEach(function(a){I.add(a)})}catch{}})}I.forEach(function(t){D.has(t)||H(t)})}}function z(e){return I?I.has(e):Fe.has(e.tagName)?!0:he.some(function(n){try{return e.matches(n)}catch{return!1}})}function Xe(e){let n="";for(const t of e.childNodes)if(t.nodeType===Node.TEXT_NODE){const r=t.textContent.trim();r&&(n+=(n?" ":"")+r)}return n}function Qe(e,n){for(var t="",r=document.createTreeWalker(e,NodeFilter.SHOW_ALL,{acceptNode:function(i){return i.nodeType===Node.ELEMENT_NODE?je.has(i.tagName)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_SKIP:NodeFilter.FILTER_ACCEPT}}),a;(a=r.nextNode())&&!(a.nodeType===Node.TEXT_NODE&&(t+=a.textContent,t.length>=n)););return t.trim()}function H(e){if(D.has(e))return D.get(e);let n=null;const t=Xe(e);if(t)return n=t.substring(0,60),D.set(e,n),n;if(Fe.has(e.tagName)){const a=(e.textContent||"").trim().replace(/\s+/g," ");if(a)return n=a.substring(0,60),D.set(e,n),n}var r=Qe(e,200);if(r){const a=r.split(/\n/)[0].trim().replace(/\s+/g," ");a.length>=2&&!E(a)&&(n=a.substring(0,60))}return D.set(e,n),n}function L(e){if(!e||!me)return"";if(ve.has(e))return ve.get(e);const n=[],t=new Set;let r=e,a=0;if(e&&e.tagName==="TD"){var i=e.closest("table");if(i){var l=i.querySelector("thead tr th:nth-child("+(e.cellIndex+1)+")");if(l){var h=H(l);h&&(t.add(h),n.push(h))}}}for(;r&&r!==document.body&&!(pe>0&&a>=pe);){a++;let d=r.previousElementSibling;for(;d;){var p=d.tagName.toUpperCase();if(S.has(p)){d=d.previousElementSibling;continue}if(p==="ARTICLE"||p==="MAIN"){d=d.previousElementSibling;continue}if(z(d)){const f=H(d);f&&!t.has(f)&&(t.add(f),n.unshift(f));break}let m=null;for(const f of d.children)if(z(f)){m=f;break}if(!m)for(const f of d.children){for(const c of f.children)if(z(c)){m=c;break}if(m)break}if(m){const f=H(m);f&&!t.has(f)&&(t.add(f),n.unshift(f));break}d=d.previousElementSibling}if(z(r)){const m=H(r);m&&!t.has(m)&&(t.add(m),n.unshift(m))}var s=r.tagName;if(s==="ARTICLE"||s==="MAIN")break;r=r.parentElement}const u=n.join(" > ");return ve.set(e,u),u}function We(e){return/^\d+$/.test(e)?"number":/^\d+\.\d+$/.test(e)?"decimal":"text"}function $e(e,n){for(var t=n.split(/(\{\{(?:text|number|decimal|date):\d+\}\})/),r=[],a=e,i=0;i<t.length;i++){var l=t[i],h=l.match(/^\{\{(text|number|decimal|date):(\d+)\}\}$/);if(h)if(a.indexOf(l)===0)r.push({type:h[1],index:parseInt(h[2]),originalText:l}),a=a.substring(l.length);else{for(var p="",s=i+1;s<t.length;s++){var u=t[s];if(/^\{\{(?:text|number|decimal|date):\d+\}\}$/.test(u)){if(a.indexOf(u)>=0){p=u;break}}else if(u!==""){p=u;break}}var d;if(p==="")d=a,a="";else{var m=a.indexOf(p);m>=0?(d=a.substring(0,m),a=a.substring(m)):(d=a,a="")}r.push({type:h[1],index:parseInt(h[2]),originalText:d})}else a.indexOf(l)===0&&(a=a.substring(l.length))}return r}function Y(e,n){e.forEach(function(t){if(!(t.varSlots&&t.varSlots.length>0)){var r=n[t.key];r&&(t.varSlots=$e(t.key,r),t.key=r)}})}function Ve(e,n){for(var t=n.split(/\{\{(?:text|number|decimal|date):\d+\}\}/g),r=e,a=[],i=0;i<t.length;i++)if(i===0)r.indexOf(t[i])===0&&(r=r.substring(t[i].length));else if(t[i]===""&&i===t.length-1)a.push(r),r="";else{var l=r.indexOf(t[i]);l>=0&&(a.push(r.substring(0,l)),r=r.substring(l+t[i].length))}return a.length>0&&a.every(function(h){return/^[a-zA-Z\s]+$/.test(h.trim())})}function qe(e){for(var n=0,t=/\{\{(text|number|decimal|date):\d+\}\}/g,r;(r=t.exec(e))!==null;)switch(r[1]){case"number":n+=3;break;case"decimal":n+=3;break;case"date":n+=2;break;default:n+=1;break}return n}function _(e,n){for(var t=[],r=0;r<n.length;r++){var a=n[r];if(!(a.indexOf("{{text:")<0&&a.indexOf("{{number:")<0&&a.indexOf("{{decimal:")<0&&a.indexOf("{{date:")<0)){for(var i=[],l=[],h=0,p=/\{\{(text|number|decimal|date):\d+\}\}/g,s;(s=p.exec(a))!==null;)i.push(a.substring(h,s.index)),l.push(s[1]),h=p.lastIndex;i.push(a.substring(h));for(var u="",d=0;d<i.length;d++)if(u+=i[d].replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),d<l.length)switch(l[d]){case"number":u+="\\d+";break;case"decimal":u+="\\d+\\.\\d+";break;default:u+=".+";break}t.push({pattern:a,regex:new RegExp("^"+u+"$"),specificity:qe(a)})}}if(t.length!==0){t.sort(function(c,g){return g.specificity-c.specificity});for(var m={},f={},r=0;r<n.length;r++)f[n[r]]=!0;return e.forEach(function(c){if(!f[c.key]&&!(c.key.length>F)){for(var g=0;g<t.length;g++)if(t[g].regex.test(c.key)&&!Ve(c.key,t[g].pattern)){m[c.key]=t[g].pattern;break}}}),Y(e,m),m}}function k(e){let n=!1,t=!1;for(const r of e.childNodes)if(r.nodeType===Node.TEXT_NODE&&r.textContent.trim()&&(n=!0),r.nodeType===Node.ELEMENT_NODE){const a=r.tagName.toUpperCase();if(a==="BR")return!1;(a==="VAR"||a==="TIME"&&r.hasAttribute("datetime")||P.has(a))&&(t=!0)}return n&&t}function ee(e){let n="";const t=[];for(const r of e.childNodes){if(r.nodeType===Node.TEXT_NODE){const a=T(r.textContent);a&&(n+=a);continue}if(r.nodeType===Node.ELEMENT_NODE){const a=r.tagName.toUpperCase();if(a==="TIME"&&r.hasAttribute("datetime")){const i=t.length;t.push({type:"date",index:i,raw:r.getAttribute("datetime"),display:r.textContent,node:r}),n+=`{{date:${i}}}`;continue}if(a==="VAR"||P.has(a)){const i=T(r.textContent);if(i){const l=t.length,h=We(i);t.push({type:h,index:l,tag:a,value:i,node:r}),n+=`{{${h}:${l}}}`}continue}}}return{key:T(n),slots:t}}var te=["title","placeholder","aria-label"];function Oe(e){const n=[],t=new Set;if(e.nodeType===Node.ELEMENT_NODE&&!B(e)&&k(e)){const{key:s,slots:u}=ee(e);if(s&&s.length<=F)if(Z(s))u.forEach(function(d){if(d.type==="text"&&d.value&&d.value.length>=2&&!E(d.value)){var m=L(d.node||e),f=d.value+"\0"+m;t.has(f)||(t.add(f),n.push({type:"text",key:d.value,slots:[],textNode:d.node?d.node.firstChild:null,element:d.node||e,context:m,original:d.value}))}});else{var r=L(e),a=s+"\0"+r;t.has(a)||t.add(a);const d=u.some(m=>m.type==="date")?"mixed-date":u.some(m=>m.type==="text")?"mixed-text":"mixed";n.push({type:d,key:s,slots:u,element:e,context:r,original:s,originalHTML:e.innerHTML})}}const i=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT,{acceptNode(s){var g,v,x;if(s.nodeType===Node.TEXT_NODE){const y=T(s.textContent);if(!y||y.length<2||y.length>F||E(y)||/\{\{(?:text|number|decimal|date):\d+\}\}/.test(y)||ke(s))return NodeFilter.FILTER_SKIP;const b=(v=(g=s.parentElement)==null?void 0:g.tagName)==null?void 0:v.toUpperCase();if(S.has(b)||k(s.parentElement))return NodeFilter.FILTER_SKIP;for(var u=s.parentElement;u;){var d=(x=u.tagName)==null?void 0:x.toUpperCase();if((d==="VAR"||d==="TIME"||P.has(d))&&u.parentElement&&k(u.parentElement))return NodeFilter.FILTER_SKIP;u=u.parentElement}return NodeFilter.FILTER_ACCEPT}if(s.nodeType===Node.ELEMENT_NODE){const y=s.tagName.toUpperCase();if(S.has(y)||B(s))return NodeFilter.FILTER_REJECT;if(y==="INPUT"){var m=(s.getAttribute("type")||"").toLowerCase();if(m==="button"||m==="submit"||m==="reset"){var f=T(s.value);if(f&&f.length>=2&&!E(f))return NodeFilter.FILTER_ACCEPT}var c=te.some(function(b){var N=T(s.getAttribute(b)||"");return N&&N.length>=2&&!E(N)});return c?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}return k(s)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}return NodeFilter.FILTER_SKIP}});let l;for(;l=i.nextNode();){if(l.nodeType===Node.TEXT_NODE){const s=T(l.textContent);if(!s)continue;var r=L(l.parentElement),a=s+"\0"+r;t.has(a)||t.add(a),n.push({type:"text",key:s,slots:[],textNode:l,element:l.parentElement,context:r,original:s});continue}if(l.nodeType===Node.ELEMENT_NODE&&l.tagName.toUpperCase()==="INPUT"){var h=(l.getAttribute("type")||"").toLowerCase(),r=L(l);if(h==="button"||h==="submit"||h==="reset"){var p=T(l.value);if(p&&p.length>=2&&!E(p)){var a=p+"\0"+r;t.has(a)||t.add(a),n.push({type:"input-value",key:p,slots:[],element:l,context:r,original:p})}}te.forEach(function(u){var d=T(l.getAttribute(u)||"");if(!(!d||d.length<2||E(d))){var m=d+"\0"+r+"\0"+u;t.has(m)||(t.add(m),n.push({type:"input-attr",key:d,attr:u,slots:[],element:l,context:r,original:d}))}});continue}if(l.nodeType===Node.ELEMENT_NODE){const{key:s,slots:u}=ee(l);if(!s||s.length>F)continue;if(Z(s)){u.forEach(function(c){if(c.type==="text"&&c.value&&c.value.length>=2&&!E(c.value)){var g=L(c.node||l),v=c.value+"\0"+g;t.has(v)||(t.add(v),n.push({type:"text",key:c.value,slots:[],textNode:c.node?c.node.firstChild:null,element:c.node||l,context:g,original:c.value}))}});continue}var r=L(l),a=s+"\0"+r;t.has(a)||t.add(a);const f=u.some(c=>c.type==="date")?"mixed-date":u.some(c=>c.type==="text")?"mixed-text":"mixed";n.push({type:f,key:s,slots:u,element:l,context:r,original:s,originalHTML:l.innerHTML})}}return n}async function j(e,n){n||(n=100);const t=[],r=new Set;if(e.nodeType===Node.ELEMENT_NODE&&!B(e)&&k(e)){const{key:f,slots:c}=ee(e);if(f&&f.length<=F)if(Z(f))c.forEach(function(g){if(g.type==="text"&&g.value&&g.value.length>=2&&!E(g.value)){var v=L(g.node||e),x=g.value+"\0"+v;r.has(x)||(r.add(x),t.push({type:"text",key:g.value,slots:[],textNode:g.node?g.node.firstChild:null,element:g.node||e,context:v,original:g.value}))}});else{var a=L(e),i=f+"\0"+a;r.has(i)||r.add(i);const g=c.some(v=>v.type==="date")?"mixed-date":c.some(v=>v.type==="text")?"mixed-text":"mixed";t.push({type:g,key:f,slots:c,element:e,context:a,original:f,originalHTML:e.innerHTML})}}const l=[],h=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT,{acceptNode(f){var b,N,A;if(f.nodeType===Node.TEXT_NODE){const w=T(f.textContent);if(!w||w.length<2||w.length>F||E(w)||/\{\{(?:text|number|decimal|date):\d+\}\}/.test(w)||ke(f))return NodeFilter.FILTER_SKIP;const K=(N=(b=f.parentElement)==null?void 0:b.tagName)==null?void 0:N.toUpperCase();if(S.has(K)||k(f.parentElement))return NodeFilter.FILTER_SKIP;for(var c=f.parentElement;c;){var g=(A=c.tagName)==null?void 0:A.toUpperCase();if((g==="VAR"||g==="TIME"||P.has(g))&&c.parentElement&&k(c.parentElement))return NodeFilter.FILTER_SKIP;c=c.parentElement}return NodeFilter.FILTER_ACCEPT}if(f.nodeType===Node.ELEMENT_NODE){const w=f.tagName.toUpperCase();if(S.has(w)||B(f))return NodeFilter.FILTER_REJECT;if(w==="INPUT"){var v=(f.getAttribute("type")||"").toLowerCase();if(v==="button"||v==="submit"||v==="reset"){var x=T(f.value);if(x&&x.length>=2&&!E(x))return NodeFilter.FILTER_ACCEPT}var y=te.some(function(K){var O=T(f.getAttribute(K)||"");return O&&O.length>=2&&!E(O)});return y?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}return k(f)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}return NodeFilter.FILTER_SKIP}});for(var p;p=h.nextNode();)l.push(p);for(var s=0;s<l.length;s++){s>0&&s%n===0&&await new Promise(function(f){setTimeout(f,0)});var u=l[s];if(u.nodeType===Node.TEXT_NODE){const f=T(u.textContent);if(!f)continue;var a=L(u.parentElement),i=f+"\0"+a;r.has(i)||r.add(i),t.push({type:"text",key:f,slots:[],textNode:u,element:u.parentElement,context:a,original:f});continue}if(u.nodeType===Node.ELEMENT_NODE&&u.tagName.toUpperCase()==="INPUT"){var d=(u.getAttribute("type")||"").toLowerCase(),a=L(u);if(d==="button"||d==="submit"||d==="reset"){var m=T(u.value);if(m&&m.length>=2&&!E(m)){var i=m+"\0"+a;r.has(i)||r.add(i),t.push({type:"input-value",key:m,slots:[],element:u,context:a,original:m})}}te.forEach(function(c){var g=T(u.getAttribute(c)||"");if(!(!g||g.length<2||E(g))){var v=g+"\0"+a+"\0"+c;r.has(v)||(r.add(v),t.push({type:"input-attr",key:g,attr:c,slots:[],element:u,context:a,original:g}))}});continue}if(u.nodeType===Node.ELEMENT_NODE){const{key:f,slots:c}=ee(u);if(!f||f.length>F)continue;if(Z(f)){c.forEach(function(y){if(y.type==="text"&&y.value&&y.value.length>=2&&!E(y.value)){var b=L(y.node||u),N=y.value+"\0"+b;r.has(N)||(r.add(N),t.push({type:"text",key:y.value,slots:[],textNode:y.node?y.node.firstChild:null,element:y.node||u,context:b,original:y.value}))}});continue}var a=L(u),i=f+"\0"+a;r.has(i)||r.add(i);const x=c.some(y=>y.type==="date")?"mixed-date":c.some(y=>y.type==="text")?"mixed-text":"mixed";t.push({type:x,key:f,slots:c,element:u,context:a,original:f,originalHTML:u.innerHTML})}}return t}function ne(e){if(o.scanStopped)return Promise.resolve({ok:!0,registered:0,keyMap:{}});var n=new Set,t=[];return e.forEach(function(r){var a=r.key+"\0"+(r.context||"");n.has(a)||r.varSlots&&r.varSlots.length>0&&/\{\{(?:text|number|decimal|date):\d+\}\}/.test(r.key)||(n.add(a),t.push({key:r.key,context:r.context||""}),r.slots&&r.slots.length>0&&r.slots.forEach(function(i){if(i.type==="text"&&i.value&&!E(i.value)){var l=i.value+"\0";n.has(l)||(n.add(l),t.push({key:i.value,context:""}))}}))}),fetch(o.apiBase+"/api/textnodes",{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":o.apiKey},body:JSON.stringify({keys:t,url:window.location.href})}).then(function(r){return r.json()})}function Ge(e){var n=o.apiBase+"/api/translations?lang="+encodeURIComponent(e);return fetch(n,{headers:{"X-API-Key":o.apiKey}}).then(function(t){return t.json()}).then(function(t){var r={};return Array.isArray(t)?t.forEach(function(a){r[a.key+"\0"+(a.context||"")]=a.value,r.hasOwnProperty(a.key)||(r[a.key]=a.value)}):r=t,r})}function Je(){if(!(o.fileMode||!o.screenshotsEnabled)){var e=document.createElement("script");e.src=o.apiBase+"/cdn/html2canvas.min.js",e.onload=function(){typeof html2canvas=="function"&&html2canvas(document.body,{scale:.35,logging:!1,useCORS:!0,allowTaint:!0,width:window.innerWidth,height:window.innerHeight,windowWidth:window.innerWidth,windowHeight:window.innerHeight}).then(function(n){var t=n.toDataURL("image/jpeg",.5),r=t.split(",")[1];!r||r.length>5e5||fetch(o.apiBase+"/api/screenshots",{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":o.apiKey},body:JSON.stringify({url:window.location.href,screenshot:r})}).catch(function(){})}).catch(function(){})},e.onerror=function(){},document.head.appendChild(e)}}function Ze(e,n){return e.replace(/\{\{(text|number|decimal|date):(\d+)\}\}/g,(t,r,a)=>{const i=n[parseInt(a)];return i?r==="date"?i.display:i.value:t})}function Pe(e){if(!Array.isArray(e))return e;var n={};return e.forEach(function(t){n[t.key+"\0"+(t.context||"")]=t.value,n.hasOwnProperty(t.key)||(n[t.key]=t.value)}),n}function ye(e,n){let t=0,r=0;return e.forEach(a=>{const i=a.key+"\0"+(a.context||"");var l=n[i]||n[a.key];if(!l&&a.slots&&a.slots.length>0){var h=a.slots.some(function(d){if(d.type!=="text"||!d.value)return!1;var m=d.value+"\0"+(a.context||""),f=d.value+"\0";return n.hasOwnProperty(m)||n.hasOwnProperty(f)||n.hasOwnProperty(d.value)});h&&(l=a.key)}if(!l){r++;return}if(a.type==="input-value"&&a.element)try{a.element.value=l,a.element.setAttribute("data-loco-translated",""),t++;return}catch(d){console.warn("[translate] Could not write to input value",d),r++;return}if(a.type==="input-attr"&&a.element&&a.attr)try{a.element.setAttribute(a.attr,l),a.element.setAttribute("data-loco-translated",""),t++;return}catch(d){console.warn("[translate] Could not write to input attr",d),r++;return}if(a.type==="text"&&a.textNode)try{var p=l;a.varSlots&&a.varSlots.length>0&&(p=p.replace(/\{\{(text|number|decimal|date):(\d+)\}\}/g,function(d,m,f){var c=a.varSlots.find(function(g){return(g.type||"text")===m&&g.index===+f});return c||(c=a.varSlots[+f]),c?c.originalText:d})),p=p.replace(/\{\{(?:text|number|decimal|date):\d+\}\}/g,""),a.textNode.nodeValue=p,a.textNode.parentElement&&a.textNode.parentElement.setAttribute("data-loco-translated",""),t++;return}catch(d){console.warn("[translate] Could not write to text node",d),r++;return}if(a.element){try{const d=Ze(l,a.slots);let m=l;var s={};a.slots.forEach(f=>{const c=`{{${f.type}:${f.index}}}`;if(f.type==="date"&&f.node)f.node.textContent=f.display,s[c]=f.node.outerHTML;else if(f.node){if(f.type==="text"&&f.value){var g=f.value+"\0"+(a.context||""),v=f.value+"\0",x=n[g]||n[v]||n[f.value];x&&(f.node.textContent=x)}s[c]=f.node.outerHTML}}),a.varSlots&&a.varSlots.length>0&&a.varSlots.forEach(function(f){var c="{{"+f.type+":"+f.index+"}}";s.hasOwnProperty(c)||(s[c]=Se(f.originalText))});var u=m.split(/(\{\{(?:text|number|decimal|date):\d+\}\})/g);m=u.map(function(f){return s.hasOwnProperty(f)?s[f]:/^\{\{(?:text|number|decimal|date):\d+\}\}$/.test(f)?"":Se(f)}).join(""),a.element.innerHTML=m,a.element.setAttribute("data-loco-translated",""),t++}catch(d){console.warn("[translate] Could not apply mixed translation",d),r++}return}r++}),{applied:t,skipped:r}}async function re(e,n,t){t||(t=100);for(var r=0,a=0,i=performance.now(),l=0;l<e.length;l+=t){l>0&&performance.now()-i>16&&(await new Promise(function(s){requestAnimationFrame(s)}),i=performance.now());var h=e.slice(l,l+t),p=ye(h,n);r+=p.applied,a+=p.skipped}return{applied:r,skipped:a}}function C(e){let n=0;e.forEach(t=>{try{t.type==="input-value"&&t.element?(t.element.value=t.original,t.element.removeAttribute("data-loco-translated"),n++):t.type==="input-attr"&&t.element&&t.attr?(t.element.setAttribute(t.attr,t.original),t.element.removeAttribute("data-loco-translated"),n++):t.type==="text"&&t.textNode&&(t.textNode.nodeValue=t.original,t.textNode.parentElement&&t.textNode.parentElement.removeAttribute("data-loco-translated"),n++)}catch(r){console.warn("[translate] Could not restore text node",r)}}),e.forEach(t=>{if(t.type!=="text")try{t.element&&t.originalHTML!==void 0&&(t.element.innerHTML=t.originalHTML,t.element.removeAttribute("data-loco-translated"),n++)}catch(r){console.warn("[translate] Could not restore node",r)}}),console.log("[loco] untranslated "+n+" phrase(s)")}function ae(e,n){const t=new Set(e),r=[];Object.values(n).forEach(function(c){if(c&&(t.add(c),/\{\{(text|number|decimal|date):\d+\}\}/.test(c))){var g=c.split(/\{\{(?:text|number|decimal|date):\d+\}\}/g),v=g.map(function(x){return x.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")});r.push(new RegExp("^"+v.join(".+")+"$"))}});function a(c){if(t.has(c))return!0;for(var g=0;g<r.length;g++)if(r[g].test(c))return!0;return!1}let i=[],l=null,h=null,p=new Set,s=null,u=!1;function d(){if(i.length!==0){var c=i.splice(0);o.fileMode||ne(c).then(function(g){g&&g.keyMap&&(Y(c,g.keyMap),u=!0,ye(c,n),s&&s.takeRecords(),u=!1)}).catch(function(){})}}function m(c){const g=Oe(c);g.forEach(v=>{var x=v.key+"\0"+(v.context||"");a(v.key)||t.has(x)||(t.add(x),t.add(v.key),i.push(v),o.phrases.push(v))}),_(g,Object.keys(n)),u=!0,ye(g,n),s&&s.takeRecords(),u=!1,o.fileMode||(clearTimeout(l),l=setTimeout(d,500))}function f(c){p.add(c),h||(h=requestAnimationFrame(function(){h=null;var g=Array.from(p);p.clear(),g.forEach(function(v){v.isConnected&&m(v)})}))}return s=new MutationObserver(c=>{if(!u){var g=[],v=new Set;for(const y of c)for(const b of y.addedNodes)if(b.nodeType===Node.ELEMENT_NODE)g.push(b);else if(b.nodeType===Node.TEXT_NODE){var x=b.parentElement;x&&x.isConnected&&v.add(x)}g.forEach(function(y){m(y)}),v.forEach(function(y){f(y)})}}),s.observe(document.body,{childList:!0,subtree:!0}),s}var De="loco",ze=2;function X(){return new Promise(function(e,n){try{var t=indexedDB.open(De,ze);t.onupgradeneeded=function(r){var a=r.target.result;a.objectStoreNames.contains("meta")&&r.oldVersion<2&&a.deleteObjectStore("meta"),a.objectStoreNames.contains("meta")||a.createObjectStore("meta",{keyPath:"key"}),a.objectStoreNames.contains("translations")||a.createObjectStore("translations",{keyPath:"lang"})},t.onsuccess=function(r){e(r.target.result)},t.onerror=function(){n(t.error)}}catch(r){n(r)}})}function oe(){return X().then(function(e){return new Promise(function(n,t){var r=e.transaction("meta","readonly"),a=r.objectStore("meta").get("registry");a.onsuccess=function(){e.close();var i=a.result;n(i?{languages:i.languages||[],languageNames:i.languageNames||{}}:null)},a.onerror=function(){e.close(),t(a.error)}})})}function Ie(e){var n="source:"+e;return X().then(function(t){return new Promise(function(r,a){var i=t.transaction("meta","readonly"),l=i.objectStore("meta").get(n);l.onsuccess=function(){t.close();var h=l.result;r(h?{url:h.url,timestamp:h.timestamp,storedAt:h.storedAt}:null)},l.onerror=function(){t.close(),a(l.error)}})})}function Ye(e){return X().then(function(n){return new Promise(function(t,r){var a=n.transaction("translations","readonly"),i=a.objectStore("translations").get(e);i.onsuccess=function(){n.close();var l=i.result;t(l?l.data:null)},i.onerror=function(){n.close(),r(i.error)}})})}function et(){return X().then(function(e){return new Promise(function(n,t){var r=e.transaction(["meta","translations"],"readonly"),a=r.objectStore("meta"),i=a.get("registry");i.onsuccess=function(){var l=i.result;if(!l||!l.languages||l.languages.length===0){e.close(),n(null);return}var h=U(),p=h||l.languages[0],s=r.objectStore("translations").get(p);s.onsuccess=function(){e.close();var u=s.result;if(!u||!u.data){n(null);return}var d={};d[p]=u.data,n({languages:l.languages,languageNames:l.languageNames||{},translations:d})},s.onerror=function(){e.close(),t(s.error)}},i.onerror=function(){e.close(),t(i.error)}})})}function ie(e,n,t){var r=n.languages||[],a=n.languageNames||{},i=n.translations||{},l=n.timestamp||0;return X().then(function(h){return new Promise(function(p,s){var u=h.transaction(["meta","translations"],"readwrite"),d=u.objectStore("meta"),m=u.objectStore("translations"),f=d.get("registry");f.onsuccess=function(){var c=f.result,g,v;if(t&&c){var x=c.languages||[];g=x.slice();for(var y=0;y<r.length;y++)g.indexOf(r[y])===-1&&g.push(r[y]);v={};var b=c.languageNames||{},N;for(N in b)v[N]=b[N];for(N in a)v[N]=a[N]}else g=r,v=a;d.put({key:"registry",languages:g,languageNames:v}),d.put({key:"source:"+e,url:e,timestamp:l,storedAt:Date.now()});for(var A=0;A<r.length;A++)i[r[A]]&&(t&&Array.isArray(i[r[A]])?function(w){var K=m.get(w);K.onsuccess=function(){var O=K.result,Q=O&&Array.isArray(O.data)?O.data:null,W=i[w];if(Q){for(var Ue="\0",$={},de=[],V=0;V<Q.length;V++){var be=Q[V].key+Ue+(Q[V].context||"");$.hasOwnProperty(be)||de.push(be),$[be]=Q[V]}for(var q=0;q<W.length;q++){var Ne=W[q].key+Ue+(W[q].context||"");$.hasOwnProperty(Ne)||de.push(Ne),$[Ne]=W[q]}for(var Be=[],Te=0;Te<de.length;Te++)Be.push($[de[Te]]);m.put({lang:w,data:Be})}else m.put({lang:w,data:W})}}(r[A]):m.put({lang:r[A],data:i[r[A]]}))},u.oncomplete=function(){h.close(),p()},u.onerror=function(){h.close(),s(u.error)}})})}function tt(){return new Promise(function(e){var n=indexedDB.deleteDatabase(De);n.onsuccess=function(){e()},n.onerror=function(){e()},n.onblocked=function(){e()}})}var nt='<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" width="32" height="32"><defs><clipPath id="loco-wc"><circle cx="100" cy="100" r="90"/></clipPath></defs><circle cx="100" cy="100" r="90" fill="#1a1f2e"/><g clip-path="url(#loco-wc)"><g transform="translate(100,108) scale(0.72) translate(-334,-198)"><ellipse cx="310" cy="318" rx="10" ry="38" fill="#2ECC88" transform="rotate(-18 310 318)"/><ellipse cx="326" cy="326" rx="9" ry="42" fill="#34D99A" transform="rotate(-6 326 326)"/><ellipse cx="342" cy="328" rx="9" ry="42" fill="#7BC74A" transform="rotate(6 342 328)"/><ellipse cx="357" cy="320" rx="9" ry="36" fill="#F07040" transform="rotate(18 357 320)"/><ellipse cx="334" cy="240" rx="62" ry="80" fill="#2ECC88"/><ellipse cx="334" cy="252" rx="36" ry="52" fill="#D4F4B0"/><path d="M275 220 Q228 190 232 265 Q248 295 280 285 Q268 255 275 220Z" fill="#3A8FE0"/><path d="M277 225 Q238 208 240 262 Q252 285 278 277 Q268 252 277 225Z" fill="#6BB3FF" opacity="0.7"/><path d="M393 220 Q440 190 436 265 Q420 295 388 285 Q400 255 393 220Z" fill="#3A8FE0"/><path d="M391 225 Q430 208 428 262 Q416 285 390 277 Q400 252 391 225Z" fill="#6BB3FF" opacity="0.7"/><ellipse cx="334" cy="172" rx="38" ry="30" fill="#2ECC88"/><circle cx="334" cy="148" r="52" fill="#2ECC88"/><ellipse cx="334" cy="118" rx="30" ry="18" fill="#FFB833"/><circle cx="312" cy="138" r="14" fill="white"/><circle cx="315" cy="140" r="9" fill="#2C2C2A"/><circle cx="315" cy="140" r="4" fill="#04342C"/><circle cx="319" cy="136" r="3.5" fill="white"/><circle cx="356" cy="138" r="14" fill="white"/><circle cx="353" cy="140" r="9" fill="#2C2C2A"/><circle cx="353" cy="140" r="4" fill="#04342C"/><circle cx="357" cy="136" r="3.5" fill="white"/><path d="M326 155 Q334 144 342 155 Q342 170 334 173 Q326 170 326 155Z" fill="#E8A020"/><path d="M328 165 Q334 158 340 165 Q340 174 334 176 Q328 174 328 165Z" fill="#A06010"/><ellipse cx="302" cy="152" rx="12" ry="8" fill="#F07040" opacity="0.8"/><ellipse cx="366" cy="152" rx="12" ry="8" fill="#F07040" opacity="0.8"/><path d="M320 100 Q316 68 308 52" stroke="#FFB833" stroke-width="6" fill="none" stroke-linecap="round"/><path d="M334 97 Q334 64 334 46" stroke="#7BC74A" stroke-width="6" fill="none" stroke-linecap="round"/><path d="M348 100 Q352 68 360 52" stroke="#F07040" stroke-width="6" fill="none" stroke-linecap="round"/><circle cx="308" cy="50" r="8" fill="#FFB833"/><circle cx="334" cy="44" r="8" fill="#7BC74A"/><circle cx="360" cy="50" r="8" fill="#F07040"/><rect x="260" y="330" width="168" height="10" rx="5" fill="#8B5E20"/><path d="M310 330 L300 350 M310 330 L315 352 M310 330 L325 348" stroke="#A07030" stroke-width="4" fill="none" stroke-linecap="round"/><path d="M360 330 L350 350 M360 330 L365 352 M360 330 L375 348" stroke="#A07030" stroke-width="4" fill="none" stroke-linecap="round"/></g></g></svg>',xe=null;function le(e,n){xe=n;var t=document.getElementById("loco-lang-widget");t&&t.remove();var r={},a=e.map(function(c){return typeof c=="object"&&c.code?(c.name&&(r[c.code]=c.name),c.code):c});function i(c){return r[c]||c}var l=U()||null,h=document.createElement("div");h.id="loco-lang-widget",h.setAttribute("data-notranslate","");var p={"bottom-right":"bottom:20px;right:20px;","bottom-left":"bottom:20px;left:20px;","top-right":"top:20px;right:20px;","top-left":"top:20px;left:20px;"};h.style.cssText='position:fixed;z-index:2147483647;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-size:14px;'+(p[n]||p["bottom-right"]);var s=document.createElement("button");s.innerHTML=nt,s.style.cssText="width:52px;height:52px;border-radius:50%;border:none;background:#1a1f2e;color:#fff;font-size:22px;cursor:pointer;box-shadow:0 4px 14px rgba(0,0,0,0.25);display:flex;align-items:center;justify-content:center;transition:transform 0.2s,box-shadow 0.2s;overflow:hidden;padding:0;",s.onmouseenter=function(){s.style.transform="scale(1.1)",s.style.boxShadow="0 6px 20px rgba(0,0,0,0.35)"},s.onmouseleave=function(){s.style.transform="scale(1)",s.style.boxShadow="0 4px 14px rgba(0,0,0,0.25)"};var u=document.createElement("div");u.style.cssText="display:none;position:absolute;"+(n.indexOf("bottom")===0?"bottom:56px;":"top:56px;")+(n.indexOf("right")>=0?"right:0;":"left:0;")+"background:#fff;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,0.18);min-width:200px;max-height:320px;overflow-y:auto;padding:6px 0;";var d=document.createElement("div");d.textContent="Original",d.style.cssText="padding:10px 16px;cursor:pointer;color:#333;transition:background 0.15s;font-weight:600;border-bottom:1px solid #eee;",d.onmouseenter=function(){d.style.background="#f5f5f5"},d.onmouseleave=function(){d.style.background="transparent"},d.onclick=function(){l=null,window.Loco.restore(),f(),u.style.display="none"},u.appendChild(d);var m=[];a.forEach(function(c){var g=document.createElement("div");g.textContent=i(c),g.setAttribute("data-lang",c),g.style.cssText="padding:10px 16px;cursor:pointer;color:#333;transition:background 0.15s;",g.onmouseenter=function(){g.style.background="#f5f5f5"},g.onmouseleave=function(){g.style.background=l===c?"#e8f0fe":"transparent"},g.onclick=function(){l=c,window.Loco.apply(c),f(),u.style.display="none"},u.appendChild(g),m.push({el:g,code:c})});function f(){d.style.background=l===null?"#e8f0fe":"transparent",d.style.fontWeight=l===null?"600":"400",m.forEach(function(c){c.el.style.background=l===c.code?"#e8f0fe":"transparent",c.el.style.fontWeight=l===c.code?"600":"400"})}s.onclick=function(c){c.stopPropagation(),u.style.display=u.style.display==="none"?"block":"none"},document.addEventListener("click",function(c){h.contains(c.target)||(u.style.display="none")}),f(),h.appendChild(u),h.appendChild(s),document.body.appendChild(h)}function rt(e){xe&&(!e||e.length===0||le(e,xe))}var R={__proto__:1,constructor:1,prototype:1};function _e(e){if(!e||typeof e!="string")return!1;if(e.charAt(0)==="/"||e.charAt(0)===".")return!0;try{var n=new URL(e,window.location.origin);return n.protocol==="http:"||n.protocol==="https:"}catch{return!1}}function se(e){if(!e||typeof e!="object"||Array.isArray(e))return null;if(Array.isArray(e._raw)){for(var n={},t={},r=0;r<e._raw.length;r++){var a=e._raw[r];!a||typeof a.l!="string"||typeof a.k!="string"||typeof a.v!="string"||(n[a.l]||(n[a.l]=!0,t[a.l]=[]),t[a.l].push({key:a.k,context:typeof a.c=="string"?a.c:"",value:a.v}))}e={languages:Object.keys(n).sort(),languageNames:{},translations:t,timestamp:0}}var i={};if(!Array.isArray(e.languages))return null;i.languages=[];for(var l=0;l<e.languages.length;l++){if(typeof e.languages[l]!="string"||e.languages[l].length>20)return null;i.languages.push(e.languages[l])}if(i.timestamp=typeof e.timestamp=="number"&&isFinite(e.timestamp)?e.timestamp:0,i.languageNames={},e.languageNames&&typeof e.languageNames=="object"&&!Array.isArray(e.languageNames))for(var h in e.languageNames)!e.languageNames.hasOwnProperty(h)||R[h]||typeof e.languageNames[h]=="string"&&e.languageNames[h].length<=100&&(i.languageNames[h]=e.languageNames[h]);if(i.translations={},e.translations&&typeof e.translations=="object"&&!Array.isArray(e.translations)){for(var p in e.translations)if(!(!e.translations.hasOwnProperty(p)||R[p])&&i.languages.indexOf(p)!==-1){var s=e.translations[p];if(Array.isArray(s)){for(var u=[],d=0;d<s.length;d++){var m=s[d];!m||typeof m!="object"||typeof m.key!="string"||typeof m.value!="string"||m.key.length>G||m.value.length>G||R[m.key]||u.push({key:m.key,value:m.value,context:typeof m.context=="string"?m.context:""})}i.translations[p]=u}else if(typeof s=="object"){var f={};for(var c in s)!s.hasOwnProperty(c)||R[c]||typeof s[c]=="string"&&(c.length>G||s[c].length>G||(f[c]=s[c]));i.translations[p]=f}}}return i}async function Re(){try{var e=await fetch(o.apiBase+"/api/project/crawler-config",{headers:{"X-API-Key":o.apiKey}});e.ok&&He(await e.json())}catch{}Ce(),o.phrases=await j(document.body),ne(o.phrases).then(function(t){t&&t.keyMap&&Y(o.phrases,t.keyMap)}).catch(function(t){console.warn("[loco] Failed to register keys:",t)}),!o.scanStopped&&o.screenshotsEnabled&&setTimeout(Je,3e3),console.log("[loco] "+o.phrases.length+" text nodes discovered"),console.log(` Loco.textnodes() — list all discovered text nodes
2
2
  Loco.apply("zh-Hans") — apply translations for a language
3
3
  Loco.restore() — revert to original text
4
- Loco.rescan() — re-scan DOM for new nodes`);var r=U();r?M.apply(r):i.observer=ae(i.phrases.map(function(t){return t.key+"\0"+(t.context||"")}),{})}function rt(e,r){for(var t="\0",n={},a=[],o=0;o<e.length;o++){var s=e[o],h=s.key+t+(s.context||"");n.hasOwnProperty(h)||a.push(h),n[h]=s}for(var m=0;m<r.length;m++){var c=r[m],u=c.key+t+(c.context||"");n.hasOwnProperty(u)||a.push(u),n[u]=c}for(var d=[],p=0;p<a.length;p++)d.push(n[a[p]]);return d}async function ce(e){i.screenshotsEnabled=!1,Ce(),i.phrases=await j(document.body);var r=[],t=i.fileData.translations||{},n=(i.fileData.languages||[])[0];n&&t[n]&&(t[n]=De(t[n]),r=Object.keys(t[n]));var a=R(i.phrases,r),o=a?Object.keys(a).length:0,s=(i.fileData.languages||[]).length;console.log("[loco] (file mode) "+i.phrases.length+" text nodes discovered, "+s+" language(s) available"+(o?", "+o+" var-remapped":"")+" ["+e+"]"),console.log(" Languages: "+(i.fileData.languages||[]).join(", "));var h=U();h?M.apply(h):i.observer=ae(i.phrases.map(function(m){return m.key+"\0"+(m.context||"")}),{})}async function Me(e){try{indexedDB.deleteDatabase("loco-translations")}catch{}try{indexedDB.deleteDatabase("loco-meta")}catch{}try{indexedDB.databases&&indexedDB.databases().then(function(t){t.forEach(function(n){if(n.name&&n.name.indexOf("loco-tr-")===0)try{indexedDB.deleteDatabase(n.name)}catch{}})}).catch(function(){})}catch{}i.fileUrls=e;var r=null;try{r=await Ye()}catch{}r&&r.translations?(i.fileData={languages:r.languages,languageNames:r.languageNames||{},translations:r.translations,timestamp:0},i.loadedFromCache=!0,await ce("cache"),i.fileReadyResolve&&(i.fileReadyResolve(),i.fileReadyResolve=null),it(e)):(await at(e),await ce("network"),i.fileReadyResolve&&(i.fileReadyResolve(),i.fileReadyResolve=null))}async function at(e){for(var r=e.map(function(m){return fetch(m).then(function(c){if(!c.ok)throw new Error("HTTP "+c.status+" for "+m);return c.text()}).then(function(c){if(!c||!c.trim())return null;var u;try{u=JSON.parse(c)}catch{return null}var d=le(u);return d?{url:m,data:d}:(console.warn("[loco] File rejected (invalid schema): "+m),null)}).catch(function(c){return console.warn("[loco] Failed to load "+m+":",c),null})}),t=await Promise.all(r),n=!0,a=0;a<t.length;a++)if(t[a]){var o=t[a].url,s=t[a].data,h=!n||(s.languages||[]).length===1;ue(s,n),n=!1;try{await oe(o,s,h)}catch{}}i.loadedFromCache=!1}function ue(e,r){if(!i.fileData||r){i.fileData={languages:(e.languages||[]).slice(),languageNames:Object.assign({},e.languageNames||{}),translations:Object.assign({},e.translations||{}),timestamp:e.timestamp||0};return}for(var t=e.languages||[],n=0;n<t.length;n++)i.fileData.languages.indexOf(t[n])===-1&&i.fileData.languages.push(t[n]);var a=e.languageNames||{};i.fileData.languageNames||(i.fileData.languageNames={});for(var o in a)!a.hasOwnProperty(o)||_[o]||(i.fileData.languageNames[o]=a[o]);var s=e.translations||{};i.fileData.translations||(i.fileData.translations={});for(var h in s)if(!(!s.hasOwnProperty(h)||_[h])){var m=i.fileData.translations[h],c=s[h];!m||!Array.isArray(m)||!Array.isArray(c)?i.fileData.translations[h]=c:i.fileData.translations[h]=rt(m,c)}e.timestamp&&e.timestamp>i.fileData.timestamp&&(i.fileData.timestamp=e.timestamp)}function it(e){var r=e.map(function(t){return Promise.all([Ie(t).catch(function(){return null}),fetch(t).then(function(n){return n.ok?n.text():null}).catch(function(){return null})]).then(function(n){var a=n[0],o=n[1];if(!o||!o.trim())return null;var s;try{s=JSON.parse(o)}catch{return null}if(s=le(s),!s)return null;var h=s.timestamp||0,m=a&&a.timestamp||0;if(h!==m){var c=e.length>1||(s.languages||[]).length===1;return oe(t,s,c).catch(function(){}),s}return null})});Promise.all(r).then(function(t){for(var n=!1,a=0;a<t.length;a++)t[a]&&(ue(t[a],!1),n=!0);n&&(i.loadedFromCache=!1,i.observer&&(i.observer.disconnect(),i.observer=null),C(i.phrases),ce("network — updated").then(function(){Ee()}),console.log("[loco] translations refreshed from file(s) (timestamp changed)"))}).catch(function(){console.log("[loco] using cached translations (network unavailable)")})}function Ee(){i.widgetPosition&&ie().then(function(e){if(e){var r=fe(e.languages,e.languageNames);nt(r)}}).catch(function(){})}function fe(e,r){return e=e||[],r=r||{},e.map(function(t){return r[t]?{code:t,name:r[t]}:t})}var M={version:"1.0.9",init:function(e){if(!e){console.warn("[loco] Loco.init() requires a config object");return}if(e.file||e.files){i.fileMode=!0,i.fileReady=new Promise(function(n){i.fileReadyResolve=n});for(var r=e.files?e.files.slice():[e.file],t=0;t<r.length;t++)if(!Re(r[t])){console.warn("[loco] Blocked unsafe URL: "+r[t]);return}i.fileUrl=r[0],document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){Me(r)}):Me(r);return}if(!e.apiKey){console.warn("[loco] Loco.init() requires { apiKey } or { file }");return}if(!e.apiUrl){console.warn("[loco] Loco.init() requires { apiUrl }");return}i.apiKey=e.apiKey,i.apiBase=e.apiUrl.replace(/\/+$/,""),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",_e):_e()},apply:async function(e){if(!e){console.warn('[loco] Loco.apply() requires a language code, e.g. Loco.apply("zh-Hans")');return}if(typeof e!="string"||e.length>20||!/^[a-zA-Z0-9\-_]+$/.test(e)){console.warn("[loco] Invalid language code: "+e);return}if(i.fileMode){if(!i.fileData){console.warn("[loco] File not loaded yet. Call Loco.init({ file }) first.");return}if(!i.fileData.translations||!i.fileData.translations[e])try{var r=await ze(e);if(!r||Object.keys(r).length===0){console.warn('[loco] No translations for "'+e+'" in file data or cache');return}return i.fileData.translations||(i.fileData.translations={}),i.fileData.translations[e]=r,i.fileData.languages.indexOf(e)===-1&&i.fileData.languages.push(e),M.apply(e)}catch(o){console.warn('[loco] Failed to load translations for "'+e+'" from cache:',o);return}i.observer&&(i.observer.disconnect(),i.observer=null),C(i.phrases),i.translations=De(i.fileData.translations[e]),i.fileData.translations[e]=i.translations,i.phrases=await j(document.body);var t=Object.keys(i.translations);R(i.phrases,t);var n=await re(i.phrases,i.translations);return i.observer=ae(i.phrases.map(function(o){return o.key+"\0"+(o.context||"")}),i.translations),J(e),console.log("[loco] (file) applied "+n.applied+" translation(s), "+n.skipped+" pending"),n}if(!i.apiKey||!i.apiBase){console.warn("[loco] Call Loco.init() first");return}i.observer&&(i.observer.disconnect(),i.observer=null),C(i.phrases);try{var a=await qe(e);i.translations=a||{},i.phrases=await j(document.body),R(i.phrases,Object.keys(i.translations)),ne(i.phrases).then(function(s){s&&s.keyMap&&(Y(i.phrases,s.keyMap),re(i.phrases,i.translations))}).catch(function(){});var n=await re(i.phrases,i.translations);return i.observer=ae(i.phrases.map(function(s){return s.key+"\0"+(s.context||"")}),i.translations),J(e),console.log("[loco] applied "+n.applied+" translation(s), "+n.skipped+" pending"),n}catch(o){console.warn("[loco] Failed to fetch translations:",o)}},restore:function(){C(i.phrases),i.observer&&i.observer.disconnect(),J(null)},rescan:async function(){var e=Object.keys(i.translations).length>0;e&&C(i.phrases),i.phrases=await j(document.body),i.fileMode||ne(i.phrases).catch(function(){}),e&&(R(i.phrases,Object.keys(i.translations)),await re(i.phrases,i.translations))},textnodes:function(){return i.phrases.map(function(e){return{key:e.key,context:e.context||"",element:e.element}})},stopScan:function(){i.scanStopped=!0,i.observer&&(i.observer.disconnect(),i.observer=null),console.log("[loco] scanning stopped, no further nodes will be sent to dashboard")},startScan:function(){i.scanStopped=!1,console.log("[loco] scanning resumed")},isFileMode:function(){return i.fileMode},languages:function(){if(i.fileMode){var e=function(){var r=i.fileData?i.fileData.languages||[]:[],t=i.fileData&&i.fileData.languageNames||{};return r.map(function(n){return{code:n,name:t[n]||n}})};return!i.fileData&&i.fileReady?i.fileReady.then(function(){return ie().then(function(r){return r&&r.languages&&r.languages.length>0?fe(r.languages,r.languageNames):e()}).catch(function(){return e()})}):ie().then(function(r){return r&&r.languages&&r.languages.length>0?fe(r.languages,r.languageNames):e()}).catch(function(){return e()})}return!i.apiKey||!i.apiBase?Promise.resolve([]):fetch(i.apiBase+"/api/languages",{headers:{"X-API-Key":i.apiKey}}).then(function(r){return r.json()}).then(function(r){return Array.isArray(r)?r:[]}).catch(function(){return[]})},widget:function(e){e=e||{};var r=e.position||"bottom-right";if(i.widgetPosition=r,i.fileMode){let t=function(){ie().then(function(a){var o;if(a&&a.languages&&a.languages.length>0?o=fe(a.languages,a.languageNames):o=n(),o.length===0){console.warn("[loco] No languages found in translation file or cache");return}se(o,r)}).catch(function(){var a=n();if(a.length===0){console.warn("[loco] No languages found in translation file");return}se(a,r)})},n=function(){var a=i.fileData?i.fileData.languages||[]:[],o=i.fileData&&i.fileData.languageNames||{};return a.map(function(s){return o[s]?{code:s,name:o[s]}:s})};if(!i.fileData&&i.fileReady){i.fileReady.then(t);return}t();return}if(!i.apiKey||!i.apiBase){console.warn("[loco] Call Loco.init() first");return}fetch(i.apiBase+"/api/languages",{headers:{"X-API-Key":i.apiKey}}).then(function(t){return t.json()}).then(function(t){if(!Array.isArray(t)||t.length===0){console.warn("[loco] No languages found — add translations in the dashboard first");return}se(t,r)}).catch(function(t){console.warn("[loco] Failed to fetch languages:",t)})},clearCache:function(){return i.fileMode?et().then(function(){i.fileData&&(i.fileData.translations={},i.fileData.timestamp=0),i.loadedFromCache=!1,J(null),C(i.phrases),i.observer&&(i.observer.disconnect(),i.observer=null),console.log("[loco] IndexedDB cache cleared — call pullLatest() to re-fetch")}).catch(function(e){console.warn("[loco] Failed to clear cache:",e)}):(console.warn("[loco] clearCache() is only available in file mode"),Promise.resolve())},addFile:function(e){return i.fileMode?e?Re(e)?(i.fileUrls.indexOf(e)===-1&&i.fileUrls.push(e),fetch(e).then(function(r){if(!r.ok)throw new Error("HTTP "+r.status);return r.text()}).then(function(r){if(!r||!r.trim())throw new Error("Empty file");var t;try{t=JSON.parse(r)}catch{throw new Error("Invalid JSON")}if(t=le(t),!t)throw new Error("Invalid file schema");var n=!i.fileData||!i.fileData.languages||i.fileData.languages.length===0,a=!n||(t.languages||[]).length===1;return ue(t,n),oe(e,t,a).catch(function(){}),Ee(),console.log("[loco] added file: "+e+" ("+(t.languages||[]).join(", ")+")"),{status:"added",languages:t.languages||[]}}).catch(function(r){return console.warn("[loco] addFile() failed:",r),{status:"error",reason:r.message}})):(console.warn("[loco] Blocked unsafe URL: "+e),Promise.resolve({status:"error",reason:"unsafe URL"})):Promise.resolve({status:"error",reason:"no URL provided"}):(console.warn("[loco] addFile() is only available in file mode"),Promise.resolve({status:"error",reason:"not in file mode"}))},pullLatest:function(){if(!i.fileMode)return console.warn("[loco] pullLatest() is only available in file mode"),Promise.resolve({status:"skipped",reason:"not in file mode"});var e=i.fileUrls.length>0?i.fileUrls:i.fileUrl?[i.fileUrl]:[];if(e.length===0)return console.warn("[loco] No file URL(s) configured. Call Loco.init({ file }) first."),Promise.resolve({status:"error",reason:"no file URLs"});var r=e.map(function(t){return Promise.all([Ie(t).catch(function(){return null}),fetch(t).then(function(n){if(!n.ok)throw new Error("HTTP "+n.status);return n.text()})]).then(function(n){var a=n[0],o=n[1];if(!o||!o.trim())return{url:t,status:"current",reason:"empty"};var s;try{s=JSON.parse(o)}catch{throw new Error("Invalid JSON in "+t)}if(s=le(s),!s)throw new Error("Invalid file schema in "+t);var h=s.timestamp||0,m=a&&a.timestamp||0;if(h===m&&m!==0){var c=i.fileData&&i.fileData.translations,u=s.languages||[],d=!c||u.some(function(l){return!i.fileData.translations[l]});if(!d)return{url:t,status:"current",timestamp:m}}var p=!i.fileData||!i.fileData.languages||i.fileData.languages.length===0,f=!p||(s.languages||[]).length===1;return ue(s,p),oe(t,s,f).catch(function(){}),{url:t,status:"updated",timestamp:h,previousTimestamp:m}}).catch(function(n){return{url:t,status:"error",reason:n.message}})});return Promise.all(r).then(async function(t){var n=t.some(function(u){return u.status==="updated"});if(n){i.loadedFromCache=!1;var a=U();if(a)i.observer&&(i.observer.disconnect(),i.observer=null),C(i.phrases),await ce("pullLatest");else{i.phrases=Oe(document.body);var o=i.fileData.translations||{},s=(i.fileData.languages||[])[0];s&&o[s]&&R(i.phrases,Object.keys(o[s]))}Ee()}if(t.length===1){var h=t[0];return console.log("[loco] pullLatest: "+h.status+(h.timestamp?" (timestamp: "+h.timestamp+")":"")),h}var m=t.filter(function(u){return u.status==="updated"}).map(function(u){return u.url}),c=t.filter(function(u){return u.status==="current"}).map(function(u){return u.url});return console.log("[loco] pullLatest: "+m.length+" updated, "+c.length+" current"),{status:n?"updated":"current",results:t}}).catch(function(t){return console.warn("[loco] pullLatest() failed:",t),{status:"error",reason:t.message}})}};return window.Loco=M,Object.defineProperty(M,"_state",{value:i,writable:!1,enumerable:!1,configurable:!1}),M}();
4
+ Loco.rescan() — re-scan DOM for new nodes`);var n=U();n?M.apply(n):o.observer=ae(o.phrases.map(function(t){return t.key+"\0"+(t.context||"")}),{})}function at(e,n){for(var t="\0",r={},a=[],i=0;i<e.length;i++){var l=e[i],h=l.key+t+(l.context||"");r.hasOwnProperty(h)||a.push(h),r[h]=l}for(var p=0;p<n.length;p++){var s=n[p],u=s.key+t+(s.context||"");r.hasOwnProperty(u)||a.push(u),r[u]=s}for(var d=[],m=0;m<a.length;m++)d.push(r[a[m]]);return d}async function ce(e){o.screenshotsEnabled=!1,Ce(),o.phrases=await j(document.body);var n=[],t=o.fileData.translations||{},r=(o.fileData.languages||[])[0];r&&t[r]&&(t[r]=Pe(t[r]),n=Object.keys(t[r]));var a=_(o.phrases,n),i=a?Object.keys(a).length:0,l=(o.fileData.languages||[]).length;console.log("[loco] (file mode) "+o.phrases.length+" text nodes discovered, "+l+" language(s) available"+(i?", "+i+" var-remapped":"")+" ["+e+"]"),console.log(" Languages: "+(o.fileData.languages||[]).join(", "));var h=U();h?M.apply(h):o.observer=ae(o.phrases.map(function(p){return p.key+"\0"+(p.context||"")}),{})}async function Me(e){try{indexedDB.deleteDatabase("loco-translations")}catch{}try{indexedDB.deleteDatabase("loco-meta")}catch{}try{indexedDB.databases&&indexedDB.databases().then(function(t){t.forEach(function(r){if(r.name&&r.name.indexOf("loco-tr-")===0)try{indexedDB.deleteDatabase(r.name)}catch{}})}).catch(function(){})}catch{}o.fileUrls=e;var n=null;try{n=await et()}catch{}n&&n.translations?(o.fileData={languages:n.languages,languageNames:n.languageNames||{},translations:n.translations,timestamp:0},o.loadedFromCache=!0,await ce("cache"),o.fileReadyResolve&&(o.fileReadyResolve(),o.fileReadyResolve=null),it(e)):(await ot(e),await ce("network"),o.fileReadyResolve&&(o.fileReadyResolve(),o.fileReadyResolve=null))}async function ot(e){for(var n=e.map(function(p){return fetch(p).then(function(s){if(!s.ok)throw new Error("HTTP "+s.status+" for "+p);return s.text()}).then(function(s){if(!s||!s.trim())return null;var u;try{u=JSON.parse(s)}catch{return null}var d=se(u);return d?{url:p,data:d}:(console.warn("[loco] File rejected (invalid schema): "+p),null)}).catch(function(s){return console.warn("[loco] Failed to load "+p+":",s),null})}),t=await Promise.all(n),r=!0,a=0;a<t.length;a++)if(t[a]){var i=t[a].url,l=t[a].data,h=!r||(l.languages||[]).length===1;ue(l,r),r=!1;try{await ie(i,l,h)}catch{}}o.loadedFromCache=!1}function ue(e,n){if(!o.fileData||n){o.fileData={languages:(e.languages||[]).slice(),languageNames:Object.assign({},e.languageNames||{}),translations:Object.assign({},e.translations||{}),timestamp:e.timestamp||0};return}for(var t=e.languages||[],r=0;r<t.length;r++)o.fileData.languages.indexOf(t[r])===-1&&o.fileData.languages.push(t[r]);var a=e.languageNames||{};o.fileData.languageNames||(o.fileData.languageNames={});for(var i in a)!a.hasOwnProperty(i)||R[i]||(o.fileData.languageNames[i]=a[i]);var l=e.translations||{};o.fileData.translations||(o.fileData.translations={});for(var h in l)if(!(!l.hasOwnProperty(h)||R[h])){var p=o.fileData.translations[h],s=l[h];!p||!Array.isArray(p)||!Array.isArray(s)?o.fileData.translations[h]=s:o.fileData.translations[h]=at(p,s)}e.timestamp&&e.timestamp>o.fileData.timestamp&&(o.fileData.timestamp=e.timestamp)}function it(e){var n=e.map(function(t){return Promise.all([Ie(t).catch(function(){return null}),fetch(t).then(function(r){return r.ok?r.text():null}).catch(function(){return null})]).then(function(r){var a=r[0],i=r[1];if(!i||!i.trim())return null;var l;try{l=JSON.parse(i)}catch{return null}if(l=se(l),!l)return null;var h=l.timestamp||0,p=a&&a.timestamp||0;if(h!==p){var s=e.length>1||(l.languages||[]).length===1;return ie(t,l,s).catch(function(){}),l}return null})});Promise.all(n).then(function(t){for(var r=!1,a=0;a<t.length;a++)t[a]&&(ue(t[a],!1),r=!0);r&&(o.loadedFromCache=!1,o.observer&&(o.observer.disconnect(),o.observer=null),C(o.phrases),ce("network — updated").then(function(){Ee()}),console.log("[loco] translations refreshed from file(s) (timestamp changed)"))}).catch(function(){console.log("[loco] using cached translations (network unavailable)")})}function Ee(){o.widgetPosition&&oe().then(function(e){if(e){var n=fe(e.languages,e.languageNames);rt(n)}}).catch(function(){})}function fe(e,n){return e=e||[],n=n||{},e.map(function(t){return n[t]?{code:t,name:n[t]}:t})}function lt(e){return e?e.type==="text"?!!(e.textNode&&e.textNode.parentElement&&e.textNode.isConnected):!!(e.element&&e.element.isConnected):!1}function Ke(){if(!Array.isArray(o.phrases)||o.phrases.length===0)return[];var e=o.phrases.filter(lt);return e.length!==o.phrases.length&&(o.phrases=e),o.phrases}var M={version:"1.0.11",init:function(e){if(!e){console.warn("[loco] Loco.init() requires a config object");return}if(e.file||e.files){o.fileMode=!0,o.fileReady=new Promise(function(r){o.fileReadyResolve=r});for(var n=e.files?e.files.slice():[e.file],t=0;t<n.length;t++)if(!_e(n[t])){console.warn("[loco] Blocked unsafe URL: "+n[t]);return}o.fileUrl=n[0],document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){Me(n)}):Me(n);return}if(!e.apiKey){console.warn("[loco] Loco.init() requires { apiKey } or { file }");return}if(!e.apiUrl){console.warn("[loco] Loco.init() requires { apiUrl }");return}o.apiKey=e.apiKey,o.apiBase=e.apiUrl.replace(/\/+$/,""),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",Re):Re()},apply:async function(e){var n=typeof window<"u"&&window.__locoPerfMode;if(n&&(performance.clearMarks(),performance.clearMeasures(),performance.mark("loco-apply-start")),!e){console.warn('[loco] Loco.apply() requires a language code, e.g. Loco.apply("zh-Hans")');return}if(typeof e!="string"||e.length>20||!/^[a-zA-Z0-9\-_]+$/.test(e)){console.warn("[loco] Invalid language code: "+e);return}if(o.fileMode){if(!o.fileData){console.warn("[loco] File not loaded yet. Call Loco.init({ file }) first.");return}if(!o.fileData.translations||!o.fileData.translations[e])try{var t=await Ye(e);if(!t||Object.keys(t).length===0){console.warn('[loco] No translations for "'+e+'" in file data or cache');return}return o.fileData.translations||(o.fileData.translations={}),o.fileData.translations[e]=t,o.fileData.languages.indexOf(e)===-1&&o.fileData.languages.push(e),M.apply(e)}catch(p){console.warn('[loco] Failed to load translations for "'+e+'" from cache:',p);return}o.observer&&(o.observer.disconnect(),o.observer=null),C(o.phrases),o.translations=Pe(o.fileData.translations[e]),o.fileData.translations[e]=o.translations,n&&performance.mark("loco-collect-start");var r=Ke();r.length===0&&(o.phrases=await j(document.body)),n&&(performance.mark("loco-collect-end"),performance.measure("loco-collection-time","loco-collect-start","loco-collect-end"));var a=Object.keys(o.translations);_(o.phrases,a),n&&performance.mark("loco-dom-start");var i=await re(o.phrases,o.translations);return n&&(performance.mark("loco-dom-end"),performance.measure("loco-dom-write-time","loco-dom-start","loco-dom-end"),performance.measure("loco-apply-total","loco-apply-start","loco-dom-end")),o.observer=ae(o.phrases.map(function(p){return p.key+"\0"+(p.context||"")}),o.translations),o.currentLang=e,J(e),console.log("[loco] (file) applied "+i.applied+" translation(s), "+i.skipped+" pending"),i}if(!o.apiKey||!o.apiBase){console.warn("[loco] Call Loco.init() first");return}o.observer&&(o.observer.disconnect(),o.observer=null),C(o.phrases);try{var l=await Ge(e);o.translations=l||{},n&&performance.mark("loco-collect-start");var h=Ke();h.length===0&&(o.phrases=await j(document.body)),n&&(performance.mark("loco-collect-end"),performance.measure("loco-collection-time","loco-collect-start","loco-collect-end")),_(o.phrases,Object.keys(o.translations)),ne(o.phrases).then(function(s){s&&s.keyMap&&(Y(o.phrases,s.keyMap),re(o.phrases,o.translations))}).catch(function(){}),n&&performance.mark("loco-dom-start");var i=await re(o.phrases,o.translations);return n&&(performance.mark("loco-dom-end"),performance.measure("loco-dom-write-time","loco-dom-start","loco-dom-end"),performance.measure("loco-apply-total","loco-apply-start","loco-dom-end")),o.observer=ae(o.phrases.map(function(s){return s.key+"\0"+(s.context||"")}),o.translations),o.currentLang=e,J(e),console.log("[loco] applied "+i.applied+" translation(s), "+i.skipped+" pending"),i}catch(p){console.warn("[loco] Failed to fetch translations:",p)}},restore:function(){C(o.phrases),o.observer&&o.observer.disconnect(),o.currentLang=null,J(null)},rescan:async function(){var e=Object.keys(o.translations).length>0;e&&C(o.phrases),o.currentLang=null,o.phrases=await j(document.body),o.fileMode||ne(o.phrases).catch(function(){}),e&&(_(o.phrases,Object.keys(o.translations)),await re(o.phrases,o.translations))},textnodes:function(){return o.phrases.map(function(e){return{key:e.key,context:e.context||"",element:e.element}})},stopScan:function(){o.scanStopped=!0,o.observer&&(o.observer.disconnect(),o.observer=null),console.log("[loco] scanning stopped, no further nodes will be sent to dashboard")},startScan:function(){o.scanStopped=!1,console.log("[loco] scanning resumed")},isFileMode:function(){return o.fileMode},languages:function(){if(o.fileMode){var e=function(){var n=o.fileData?o.fileData.languages||[]:[],t=o.fileData&&o.fileData.languageNames||{};return n.map(function(r){return{code:r,name:t[r]||r}})};return!o.fileData&&o.fileReady?o.fileReady.then(function(){return oe().then(function(n){return n&&n.languages&&n.languages.length>0?fe(n.languages,n.languageNames):e()}).catch(function(){return e()})}):oe().then(function(n){return n&&n.languages&&n.languages.length>0?fe(n.languages,n.languageNames):e()}).catch(function(){return e()})}return!o.apiKey||!o.apiBase?Promise.resolve([]):fetch(o.apiBase+"/api/languages",{headers:{"X-API-Key":o.apiKey}}).then(function(n){return n.json()}).then(function(n){return Array.isArray(n)?n:[]}).catch(function(){return[]})},widget:function(e){e=e||{};var n=e.position||"bottom-right";if(o.widgetPosition=n,o.fileMode){let t=function(){oe().then(function(a){var i;if(a&&a.languages&&a.languages.length>0?i=fe(a.languages,a.languageNames):i=r(),i.length===0){console.warn("[loco] No languages found in translation file or cache");return}le(i,n)}).catch(function(){var a=r();if(a.length===0){console.warn("[loco] No languages found in translation file");return}le(a,n)})},r=function(){var a=o.fileData?o.fileData.languages||[]:[],i=o.fileData&&o.fileData.languageNames||{};return a.map(function(l){return i[l]?{code:l,name:i[l]}:l})};if(!o.fileData&&o.fileReady){o.fileReady.then(t);return}t();return}if(!o.apiKey||!o.apiBase){console.warn("[loco] Call Loco.init() first");return}fetch(o.apiBase+"/api/languages",{headers:{"X-API-Key":o.apiKey}}).then(function(t){return t.json()}).then(function(t){if(!Array.isArray(t)||t.length===0){console.warn("[loco] No languages found — add translations in the dashboard first");return}le(t,n)}).catch(function(t){console.warn("[loco] Failed to fetch languages:",t)})},clearCache:function(){return o.fileMode?tt().then(function(){o.fileData&&(o.fileData.translations={},o.fileData.timestamp=0),o.currentLang=null,o.loadedFromCache=!1,J(null),C(o.phrases),o.observer&&(o.observer.disconnect(),o.observer=null),console.log("[loco] IndexedDB cache cleared — call pullLatest() to re-fetch")}).catch(function(e){console.warn("[loco] Failed to clear cache:",e)}):(console.warn("[loco] clearCache() is only available in file mode"),Promise.resolve())},addFile:function(e){return o.fileMode?e?_e(e)?(o.fileUrls.indexOf(e)===-1&&o.fileUrls.push(e),fetch(e).then(function(n){if(!n.ok)throw new Error("HTTP "+n.status);return n.text()}).then(function(n){if(!n||!n.trim())throw new Error("Empty file");var t;try{t=JSON.parse(n)}catch{throw new Error("Invalid JSON")}if(t=se(t),!t)throw new Error("Invalid file schema");var r=!o.fileData||!o.fileData.languages||o.fileData.languages.length===0,a=!r||(t.languages||[]).length===1;return ue(t,r),ie(e,t,a).catch(function(){}),Ee(),console.log("[loco] added file: "+e+" ("+(t.languages||[]).join(", ")+")"),{status:"added",languages:t.languages||[]}}).catch(function(n){return console.warn("[loco] addFile() failed:",n),{status:"error",reason:n.message}})):(console.warn("[loco] Blocked unsafe URL: "+e),Promise.resolve({status:"error",reason:"unsafe URL"})):Promise.resolve({status:"error",reason:"no URL provided"}):(console.warn("[loco] addFile() is only available in file mode"),Promise.resolve({status:"error",reason:"not in file mode"}))},pullLatest:function(){if(!o.fileMode)return console.warn("[loco] pullLatest() is only available in file mode"),Promise.resolve({status:"skipped",reason:"not in file mode"});var e=o.fileUrls.length>0?o.fileUrls:o.fileUrl?[o.fileUrl]:[];if(e.length===0)return console.warn("[loco] No file URL(s) configured. Call Loco.init({ file }) first."),Promise.resolve({status:"error",reason:"no file URLs"});var n=e.map(function(t){return Promise.all([Ie(t).catch(function(){return null}),fetch(t).then(function(r){if(!r.ok)throw new Error("HTTP "+r.status);return r.text()})]).then(function(r){var a=r[0],i=r[1];if(!i||!i.trim())return{url:t,status:"current",reason:"empty"};var l;try{l=JSON.parse(i)}catch{throw new Error("Invalid JSON in "+t)}if(l=se(l),!l)throw new Error("Invalid file schema in "+t);var h=l.timestamp||0,p=a&&a.timestamp||0;if(h===p&&p!==0){var s=o.fileData&&o.fileData.translations,u=l.languages||[],d=!s||u.some(function(c){return!o.fileData.translations[c]});if(!d)return{url:t,status:"current",timestamp:p}}var m=!o.fileData||!o.fileData.languages||o.fileData.languages.length===0,f=!m||(l.languages||[]).length===1;return ue(l,m),ie(t,l,f).catch(function(){}),{url:t,status:"updated",timestamp:h,previousTimestamp:p}}).catch(function(r){return{url:t,status:"error",reason:r.message}})});return Promise.all(n).then(async function(t){var r=t.some(function(u){return u.status==="updated"});if(r){o.loadedFromCache=!1,o.currentLang=null;var a=U();if(a)o.observer&&(o.observer.disconnect(),o.observer=null),C(o.phrases),await ce("pullLatest");else{o.phrases=Oe(document.body);var i=o.fileData.translations||{},l=(o.fileData.languages||[])[0];l&&i[l]&&_(o.phrases,Object.keys(i[l]))}Ee()}if(t.length===1){var h=t[0];return console.log("[loco] pullLatest: "+h.status+(h.timestamp?" (timestamp: "+h.timestamp+")":"")),h}var p=t.filter(function(u){return u.status==="updated"}).map(function(u){return u.url}),s=t.filter(function(u){return u.status==="current"}).map(function(u){return u.url});return console.log("[loco] pullLatest: "+p.length+" updated, "+s.length+" current"),{status:r?"updated":"current",results:t}}).catch(function(t){return console.warn("[loco] pullLatest() failed:",t),{status:"error",reason:t.message}})}};return window.Loco=M,Object.defineProperty(M,"_state",{value:o,writable:!1,enumerable:!1,configurable:!1}),M}();
package/versions.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.0.9"
2
+ "version": "1.0.11"
3
3
  }