assets/js/main.min.js
(()=>{const t=Number(window.BRIVACIA_AUTO_REFRESH_SECONDS||0);t<=0||window.setInterval(()=>{Boolean(document.querySelector("dialog[open], dialog.modal[open], .modal[open], .modal.is-open")||"1"===document.body.dataset.updating)||window.location.reload()},1e3*t)})();const isIOS=/iPad|iPhone|iPod/.test(navigator.userAgent)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1;if(isIOS){document.documentElement.classList.add("ios");const t=document.getElementById("period");t?.dataset.iosType&&(t.type=t.dataset.iosType,t.value=t.dataset.iosValue??t.value)}const dateButton=document.querySelector("button[data-period-type]"),dateInput=document.querySelector('.date-form input:not([type="hidden"])');dateButton?.addEventListener("click",()=>{dateInput?.showPicker?.()});const dropdowns=document.querySelectorAll("[data-dropdown]");function closeMenus(t="[data-dropdown-menu], [data-dropdown-submenu]"){document.querySelectorAll(t).forEach(t=>{t.hidden=!0})}dropdowns.forEach(t=>{const e=t.querySelector(":scope > [data-dropdown-toggle]"),a=t.querySelector(":scope > [data-dropdown-menu], :scope > [data-dropdown-submenu]");e&&a&&e.addEventListener("click",e=>{e.stopPropagation();const o=a.hasAttribute("data-dropdown-submenu"),n=a.hidden;o?t.parentElement?.querySelectorAll(":scope > [data-dropdown] > [data-dropdown-submenu]").forEach(t=>{t!==a&&(t.hidden=!0)}):closeMenus(),a.hidden=!n})}),document.addEventListener("click",()=>closeMenus()),document.addEventListener("keydown",t=>{"Escape"===t.key&&closeMenus()});const lang=navigator.language,dateFormatter=new Intl.DateTimeFormat(lang,{year:"numeric",month:"2-digit",day:"2-digit"}),timeFormatter=new Intl.DateTimeFormat(lang,{hour:"numeric",minute:"2-digit"}),countryNames=new Intl.DisplayNames([lang],{type:"region"});function formatPeriodLabel(t,e=new Date){const a=t.dataset.periodType,o=t.dataset.periodValue;if("today"===a)return dateFormatter.format(e);if("week"===a){const e=o.split("-W")[1]??o;return`${t.dataset.weekLabel??"Week"} ${e}`}if("month"===a){const t=new Intl.DateTimeFormat(lang,{year:"numeric",month:"long"}).format(new Date(o+"-01T00:00:00"));return t.charAt(0).toUpperCase()+t.slice(1)}return"year"===a?o.slice(0,4):"all"===a?t.dataset.allLabel??t.textContent.trim():t.textContent.trim()}function updateDateTime(){const t=new Date,e=dateFormatter.format(t),a=timeFormatter.format(t);for(const t of document.querySelectorAll("[data-live-date]"))t.textContent=e;for(const t of document.querySelectorAll("[data-current-time]"))t.textContent=a}updateDateTime(),setInterval(updateDateTime,1e3);for(const t of document.querySelectorAll("span[data-country]"))"XX"!==t.dataset.country&&(t.textContent=countryNames.of(t.dataset.country)??t.dataset.country);function openModal(t){document.querySelector(t)?.closest(".modal")?.showModal()}function blurModalFields(t){t?.querySelectorAll("input, select, textarea, button")?.forEach(t=>t.blur())}function closeModal(t){t.target.classList.contains("modal")&&(blurModalFields(t.target),t.target.close())}for(const t of document.querySelectorAll(".modal"))t.addEventListener("click",closeModal),t.addEventListener("close",()=>{blurModalFields(t),document.body.style.overflow=""});let importsLoaded=!1;async function loadImports(t=document.getElementById("import-modal")){if(!importsLoaded){importsLoaded=!0;try{await loadScript("/assets/js/imports.min.js")}catch(t){throw importsLoaded=!1,t}}window.brivaciaSetImportProvider?.(t?.dataset.importProvider||"matomo")}for(const t of document.querySelectorAll("[data-modal-open]"))t.addEventListener("click",async()=>{const e=document.getElementById(t.dataset.modalOpen);e&&(t.dataset.importProvider&&(e.dataset.importProvider=t.dataset.importProvider),e.showModal(),e.hasAttribute("data-import")&&loadImports(e),e.hasAttribute("data-prism")&&loadPrism(),requestAnimationFrame(()=>{blurModalFields(e),e.scrollTo?.(0,0),e.querySelector(":scope > div")?.scrollTo?.(0,0)}))});(()=>{const t=document.getElementById("settings-form"),e=document.getElementById("settings-sites"),a=document.getElementById("settings-site-template"),o=document.getElementById("settings-message"),n={saved:o?.dataset.saved??"Settings saved.",error:o?.dataset.error??"Unable to save settings.",networkError:o?.dataset.networkError??"Network error."};function r(t,e=""){o&&(o.className=e,o.textContent=t||"")}function i(){t.closest("dialog")?.close()}function l(){return e?.querySelector(":scope > div")||null}function s(t){const e=l(),a=t.closest("fieldset");!e||!a||e.children.length<=1||(a.remove(),function(){const t=l();t&&[...t.children].forEach((t,e)=>{const a=t.querySelector('input[name$="[code]"]'),o=t.querySelector('input[name$="[domain]"]');a&&(a.name=`sites[${e}][code]`),o&&(o.name=`sites[${e}][domain]`)})}())}function d(){const e=new FormData(t),a={};for(const[t,o]of e.entries())t.startsWith("sites[")||(a[t]=o);return t.querySelectorAll('input[type="checkbox"]').forEach(t=>{a[t.name]=t.checked}),a.sites=[],l()?.querySelectorAll(":scope > fieldset").forEach(t=>{const e=t.querySelector('input[name$="[code]"]')?.value.trim()||"",o=t.querySelector('input[name$="[domain]"]')?.value.trim()||"";""===e&&""===o||a.sites.push({code:e,domain:o})}),a}t&&(t.addEventListener("click",t=>{const e=t.target.closest("button");e&&("cancel"!==e.value?"add-site"!==e.value?"remove-site"===e.value&&s(e):function(){const t=l();if(!t||!a)return;const e=t.children.length,o=document.createElement("div");o.innerHTML=a.innerHTML.replaceAll("__INDEX__",String(e)).trim(),o.firstElementChild&&t.appendChild(o.firstElementChild)}():i())}),t.addEventListener("submit",async t=>{t.preventDefault(),r("");try{const t=await fetch("/api/settings.php?action=save",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d())}),e=await t.json();if(!t.ok||!e.ok)return void r(e.error||n.error,"danger");r(n.saved,"success"),setTimeout(()=>{i(),window.location.reload()},600)}catch{r(n.networkError,"warning")}}))})();const graph=document.querySelector(".graph"),graphText={error:graph?.dataset.error??"Unable to generate the chart",generating:graph?.dataset.generating??"Generating chart...",noData:graph?.dataset.noData??"No data available"};function graphModal(){return document.querySelector("dialog.modal:has(.graph)")}function resetGraphModal(){const t=graphModal();t&&(t.id="graph-modal",t.removeAttribute("data-graph"))}async function loadGraph(t){if(graph){graph.textContent=graphText.generating;try{const e=new URLSearchParams(window.location.search),a=navigator.language||document.documentElement.lang||"en";e.set("metric",t),e.set("lang",a);const o=await fetch(`/api/graphs.php?${e.toString()}`);if(!o.ok){const t=(await o.text()).trim();return void(graph.textContent=""!==t?t:graphText.error)}const n=await o.text();graph.innerHTML=""!==n.trim()?n:graphText.noData}catch{graph.textContent=graphText.error}}}graphModal()?.addEventListener("close",resetGraphModal);for(const t of document.querySelectorAll("[data-graph]"))t.addEventListener("click",async()=>{const e=t.dataset.graph,a=document.getElementById("graph-modal");a&&e&&(a.id=e,a.dataset.graph=e),openModal(".graph"),await loadGraph(e)});let mapScale=1,mapTranslateX=0,mapTranslateY=0,mapTouchDistance=null,mapDragging=!1,mapDragStartX=0,mapDragStartY=0;function applyMapTransform(t){t.querySelector("svg").style.transform=`translate(${mapTranslateX}px, ${mapTranslateY}px) scale(${mapScale})`}function setMapScale(t,e){mapScale=Math.max(1,Math.min(e,8)),1===mapScale&&(mapTranslateX=0,mapTranslateY=0),applyMapTransform(t)}function touchDistance(t){const e=t[0].clientX-t[1].clientX,a=t[0].clientY-t[1].clientY;return Math.hypot(e,a)}function setMapOriginFromPoint(t,e,a){const o=t.querySelector("svg"),n=o.getBoundingClientRect(),r=(e-n.left)/n.width*100,i=(a-n.top)/n.height*100;o.style.transformOrigin=`${r}% ${i}%`}document.addEventListener("wheel",t=>{const e=t.target.closest(".graph-map");e&&(t.preventDefault(),setMapOriginFromPoint(e,t.clientX,t.clientY),setMapScale(e,mapScale*(t.deltaY>0?.9:1.1)))},{passive:!1}),document.addEventListener("pointerdown",t=>{!t.target.closest(".graph-map")||mapScale<=1||"touch"===t.pointerType||(mapDragging=!0,mapDragStartX=t.clientX-mapTranslateX,mapDragStartY=t.clientY-mapTranslateY)}),document.addEventListener("pointermove",t=>{const e=t.target.closest(".graph-map");e&&mapDragging&&(mapTranslateX=t.clientX-mapDragStartX,mapTranslateY=t.clientY-mapDragStartY,applyMapTransform(e))}),document.addEventListener("pointerup",()=>{mapDragging=!1}),document.addEventListener("touchstart",t=>{if(t.target.closest(".graph-map"))return 2===t.touches.length?(t.preventDefault(),void(mapTouchDistance=touchDistance(t.touches))):void(1===t.touches.length&&mapScale>1&&(t.preventDefault(),mapDragging=!0,mapDragStartX=t.touches[0].clientX-mapTranslateX,mapDragStartY=t.touches[0].clientY-mapTranslateY))},{passive:!1}),document.addEventListener("touchmove",t=>{const e=t.target.closest(".graph-map");if(e){if(2===t.touches.length&&null!==mapTouchDistance){t.preventDefault();const a=touchDistance(t.touches),o=a/mapTouchDistance;return setMapOriginFromPoint(e,(t.touches[0].clientX+t.touches[1].clientX)/2,(t.touches[0].clientY+t.touches[1].clientY)/2),setMapScale(e,mapScale*o),void(mapTouchDistance=a)}1===t.touches.length&&mapDragging&&mapScale>1&&(t.preventDefault(),mapTranslateX=t.touches[0].clientX-mapDragStartX,mapTranslateY=t.touches[0].clientY-mapDragStartY,applyMapTransform(e))}},{passive:!1}),document.addEventListener("touchend",()=>{mapTouchDistance=null,mapDragging=!1});let tooltipElement=null,tooltipTarget=null;function tooltipTargetFrom(t){let e=t.target;for(;e&&e!==document;){if(e.dataset?.tooltip)return e;e=e.parentNode}return null}function openTooltip(t,e=null){closeTooltip(),tooltipTarget=t,tooltipElement=document.createElement("div"),tooltipElement.setAttribute("data-tooltip-open",""),t.dataset.tooltipClass&&tooltipElement.classList.add(t.dataset.tooltipClass),t.dataset.tooltipIcon?tooltipElement.innerHTML=`<img alt="" class="tooltip-icon" src="${t.dataset.tooltipIcon}"><span>${t.dataset.tooltip??""}</span>`:tooltipElement.textContent=t.dataset.tooltip??"",(t.closest("dialog")??document.body).appendChild(tooltipElement);const a=parseFloat(getComputedStyle(document.documentElement).fontSize),o=a,n=t.getBoundingClientRect(),r=tooltipElement.getBoundingClientRect();if("graph-tooltip"===t.dataset.tooltipClass&&e&&!t.dataset.tooltipPlacement){const n=(t.closest("dialog")??document.body).getBoundingClientRect();let i=e.clientX-r.width/2-n.left,l=e.clientY+o-n.top;return e.clientY+o+r.height>window.innerHeight-a&&(l=e.clientY-r.height-o-n.top),i=Math.max(a,Math.min(i,n.width-r.width-a)),tooltipElement.style.setProperty("--tooltip-left",`${i}px`),tooltipElement.style.setProperty("--tooltip-top",`${l}px`),tooltipElement.setAttribute("data-tooltip-placement","graph"),void requestAnimationFrame(()=>{tooltipElement?.setAttribute("data-tooltip-visible","")})}let i,l,s=t.dataset.tooltipPlacement??"bottom";"left"===s&&window.matchMedia("(hover: none)").matches&&(s="top"),"left"===s?(i=n.left-r.width-o,l=n.top+n.height/2-r.height/2):"right"===s?(i=n.right+o,l=n.top+n.height/2-r.height/2):(i=n.left+n.width/2-r.width/2,l=n.bottom+o,l+r.height>window.innerHeight-a&&(l=n.top-r.height-o,s="top")),i=Math.max(a,Math.min(i,window.innerWidth-r.width-a)),l=Math.max(a,Math.min(l,window.innerHeight-r.height-a)),tooltipElement.setAttribute("data-tooltip-placement",s),tooltipElement.style.setProperty("--tooltip-left",`${i}px`),tooltipElement.style.setProperty("--tooltip-top",`${l}px`),requestAnimationFrame(()=>{tooltipElement?.setAttribute("data-tooltip-visible","")})}function closeTooltip(){if(!tooltipElement)return;const t=tooltipElement;t.removeAttribute("data-tooltip-visible"),setTimeout(()=>{t.remove()},120),tooltipElement=null,tooltipTarget=null}document.addEventListener("pointerover",t=>{const e=tooltipTargetFrom(t);e&&tooltipTarget!==e&&openTooltip(e,t)}),document.addEventListener("pointerout",t=>{const e=tooltipTargetFrom(t);e&&(t.relatedTarget&&e.contains(t.relatedTarget)||closeTooltip())}),document.addEventListener("focusin",t=>{const e=tooltipTargetFrom(t);e&&openTooltip(e)}),document.addEventListener("focusout",t=>{tooltipTargetFrom(t)&&closeTooltip()}),document.addEventListener("pointerdown",t=>{tooltipTargetFrom(t)||closeTooltip()});let prismLoaded=!1;async function loadPrism(){prismLoaded||(prismLoaded=!0,await Promise.all([loadScript("/assets/js/prism.min.js")]),Prism.highlightAllUnder(document.getElementById("pixel-modal")))}function loadScript(t){return new Promise((e,a)=>{const o=document.createElement("script");o.src=t,o.onload=e,o.onerror=a,document.head.appendChild(o)})}const privacyButton=document.querySelector("[data-privacy-open]"),browserLanguage=document.querySelector("[data-browser-language]");privacyButton?.addEventListener("click",()=>{browserLanguage&&(browserLanguage.textContent=navigator.language||"no data"),openModal(".privacy")}),(()=>{const t=document.getElementById("update-install");if(!t)return;const e=t.innerHTML,a=document.getElementById("update-available"),o=document.getElementById("update-progress"),n=document.getElementById("update-progress-text"),r={download:t.dataset.updateDownload,extract:t.dataset.updateExtract,verify:t.dataset.updateVerify,install:t.dataset.updateInstalling,cleanup:t.dataset.updateCleanup};let i=null;const l=async()=>{try{const t=await fetch("/api/update.php?progress=1",{cache:"no-store"}),e=await t.json();e.running&&e.step&&r[e.step]&&(n.textContent=r[e.step])}catch{}};t.addEventListener("click",async r=>{if(r.preventDefault(),r.stopImmediatePropagation(),console.log("Brivacia update clicked"),!t.disabled){t.disabled=!0,document.body.dataset.updating="1",a.hidden=!0,o.hidden=!1,n.textContent=t.dataset.updateStarting,i=setInterval(l,400);try{console.log("Brivacia update POST start");const e=await fetch("/api/update.php",{method:"POST",cache:"no-store"});console.log("Brivacia update POST response",e.status);const a=await e.text();console.log("Brivacia update raw response",a);const o=JSON.parse(a);if(!e.ok||!o.ok)throw new Error(o.error||t.dataset.updateFailed);clearInterval(i),n.textContent=t.dataset.updateReload,await new Promise(t=>setTimeout(t,1200)),window.location.reload()}catch(n){console.error("Brivacia update failed",n),clearInterval(i),delete document.body.dataset.updating,t.disabled=!1,a.hidden=!1,o.hidden=!0,t.innerHTML=e,alert(n.message||t.dataset.updateFailed)}}})})();