-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.min.js
More file actions
1 lines (1 loc) · 24.8 KB
/
script.min.js
File metadata and controls
1 lines (1 loc) · 24.8 KB
1
let rebootTimeoutId,saveConfigTimeOutId,peerTimeOutId={};const peerTimeoutMs=1e4;let webSocket,webSocketReconnect,onSphereUp,onSphereDown,webSocketConnected=!1,onWebSocketConnectedOneTime=null,config={};const maxWifiNetworks=-1;let ssidList=[],statuscode=128;const frameRate=20;let warmth=0,targetWarmth=0;const maxWarmth=5;let peakEnergy=0;const tuningState={running:!1,goodPeakFound:!1,timeOutId:void 0},narrowFilterBandwidthHz=20;let mic={};const numberOfHistogramBins=8,histogram=Array(8).fill(0);let micMinFreq,micMaxFreq;const tuneWindowMs=3e3,lowMicSampleRate=1,highMicSampleRate=5,minHistogramPeakValue=1;let audioCtx,onTouchOneTime=null,onMovedOneTime=null;const peers=[];let lastSplideIndex=-1;const enableSwipe=!0;let deferredInstallPrompt,micPostLocked=!0;const requestQueue={pending:new Set,maxConcurrent:3,async execute(e,t){if(this.pending.has(e))return console.log(`Skipping duplicate request: ${e}`),null;for(;this.pending.size>=this.maxConcurrent;)await new Promise(e=>setTimeout(e,100));this.pending.add(e);try{return await t()}finally{this.pending.delete(e)}}};async function init(){document.querySelectorAll("img").forEach(e=>{e.addEventListener("error",function(){setTimeout(()=>{const t=new URL(e.src,location.href);t.searchParams.set("_retry",Date.now()),e.src=t.toString(),console.log("Retrying image load:",e.src)},5e3)})}),document.addEventListener("keydown",e=>{e.repeat||onKeyPressed(e.key,!0)}),document.addEventListener("keyup",e=>{onKeyPressed(e.key,!1)}),initSphere()}function configureUIEvents(){document.addEventListener("focusin",function(e){e.target?.closest&&e.target.closest(".yo-yo-form")&&allowSwipe(!1)},!0),document.addEventListener("focusout",function(e){const t=e.target?.closest&&e.target.closest(".yo-yo-form"),n=e.relatedTarget&&e.relatedTarget.closest&&e.relatedTarget.closest(".yo-yo-form")===e.target.closest(".yo-yo-form");t&&!n&&allowSwipe(!0)},!0),window.addEventListener("resize",debounce(()=>{positionSphereImage(),updateSlide()},300));const e=document.getElementById("server_form");e.addEventListener("submit",function(t){t.preventDefault(),onServerSaveEvent(new FormData(e))});const t=document.getElementById("wifi_form");t.addEventListener("submit",function(e){console.log("wifiForm.addEventListener()"),e.preventDefault(),onWiFiSaveEvent(new FormData(t))});const n=document.getElementById("determination_individual"),o=debounce(e=>{console.log("determinationListener");const t={server:{...config.server??{}}};t.server.room={...t.server.room??{}},t.server.room.determination=e.target.value,setConfiguration(t)},2e3);n.addEventListener("input",o),splide.on("moved",e=>{onMovedOneTime&&(onMovedOneTime(),onMovedOneTime=void 0)}),lastSplideIndex=splide.index,splide.on("active",e=>{lastSplideIndex!==splide.index&&updateSlide(!0),sphereIsOnline()&&(lastSplideIndex=splide.index)})}function hasConfiguration(){return"object"==typeof config&&Object.keys(config).length>0}async function initSphere(){if(console.log("initSphere"),splide=new Splide("#carousel",{type:"slide",perPage:1,drag:!1}).mount(),showCarousel(!1),positionSphereImage(),allowInteraction(!1),console.log("configureUIEvents"),configureUIEvents(),console.log("configureUIEvents - done"),console.log("getConfiguration"),await getConfiguration())console.log("getConfiguration - done"),document.getElementById("spherename").innerText=config?.captiveportal?.ssid??"",document.getElementById("sphereversion").innerText=config?.version??"",setInterval(()=>{loop()},50),setInterval(()=>{onTick()},1e4),manageWebSocket(()=>onStart()),updateSlide(!0);else{console.log("getConfiguration - error");getSlideByID(getSlideId()).querySelectorAll(".slide-content .row")[0].innerHTML="error"}}function onKeyPressed(e,t){if("landing"===getSlideId()){const e=Number(event.key);isNaN(e)||onUserClicked({track:e},t)}}function positionSphereImage(){const e=document.querySelector(".middle-row"),t=document.querySelector(".background-image");if(e&&t){const n=e.getBoundingClientRect();t.style.top=`${n.top+window.scrollY}px`,t.style.height=`${n.height}px`}}function debounce(e,t){let n;return function(...o){clearTimeout(n),n=setTimeout(()=>e.apply(this,o),t)}}function onTuningComplete(){if(tuningState.running){console.log("stop tuning");const e=getGoodHistogramPeak(histogram,1*(mic?.level??1));if(console.log("getGoodHistogramPeak ",e),e.frequency>-1&&peakEnergy>0){const t=(mic?.level??1)*(5/(.9*peakEnergy));setMic({frequency:e.frequency,level:parseFloat(t.toFixed(2))},!0),tuningState.goodPeakFound=!0,updateSlide()}}tuningState.timeOutId=void 0,peakEnergy=0,tuningState.goodPeakFound&&setTimeout(()=>{void 0===tuningState.timeOutId&&showNextSlide()},3e3)}function loop(){"tuning"!==getSlideId()||tuningState.timeOutId||isCold()||(console.log("start tuning"),clearHistogram(histogram),tuningState.timeOutId=setTimeout(()=>{onTuningComplete()},3e3),updateSlide()),draw()}function draw(){if(sphereIsUp()){warmth+=.1*(targetWarmth-warmth),setBackgroundFromValue(51*Math.min(Math.max(warmth,0),5))}else setBackgroundFromValue(0)}function onTick(){getStatus()}async function fetchWithTimeout(e,t=5e3,n={}){return requestQueue.execute(e,async()=>{const o=new AbortController,i=setTimeout(()=>o.abort(),t);try{const t=await fetch(e,{signal:o.signal,...n});return clearTimeout(i),t}catch(n){if(clearTimeout(i),n&&"AbortError"===n.name)throw new Error(`Fetch timeout (${t}ms): ${e}`);throw n}})}async function getStatus(){let e=254&statuscode;try{const t=await fetchWithTimeout("/yoyo/status",3e3);if(t.ok){const n=await t.json();e=Number(n.statuscode),e=webSocketConnected?8|e:e,updateCount(n?.count??0)}}catch(e){}e!==statuscode&&onStatus(e)}function sphereWillReboot(){return rebootTimeoutId}function reboot(){const e="landing";showSlideID(e);const t=getSlideByID(e).querySelectorAll(".slide-content .row"),n=config?.wifi?.ssid??"";t[0].innerHTML="Rebooting...",t[2].innerHTML="Now close this window. Then make sure this "+getDeviceType()+" is on "+(""!==n?"the "+n:"that")+" network too and scan the new QR code when the sphere has restarted.",postJson("/yoyo/reboot",{},500)}function sphereIsOnline(e=statuscode){return!(1&~e)}function sphereIsUp(e=statuscode){return!(2&~e)}function captivePortalRunning(e=statuscode){return!(4&~e)}function localConnected(e=statuscode){return!(8&~e)}function remoteConnected(e=statuscode){return!(16&~e)}function drawSphere(e){const t=document.querySelector("#sphereImage");sphereIsUp(e)?t.src="img/sphere-up.png":t.src="img/sphere-down.png"}function onStatus(e){if(statuscode!=e){const t=statuscode;if(statuscode=e,sphereIsUp(e)?(onSphereUp&&"function"==typeof onSphereUp&&onSphereUp(),onSphereUp=void 0):(onSphereDown&&"function"==typeof onSphereDown&&onSphereDown(),onSphereDown=void 0),!sphereIsOnline(t)&&sphereIsOnline()&&onOnline(),sphereIsOnline(t)&&!sphereIsOnline()&&onOffline(),"landing"===getSlideId())getSlideByID("landing").querySelectorAll(".slide-content .row")[2].querySelector("span").innerHTML=generateLandingText();drawSphere(statuscode)}}function onWebSocketConnected(e=!0){webSocketConnected!==e&&(e&&(onStatus(9|statuscode),onWebSocketConnectedOneTime&&(onWebSocketConnectedOneTime(),onWebSocketConnectedOneTime=null)),allowInteraction(e),webSocketConnected=e,updateSlide())}async function onOnline(){console.log("onOnline",config),rebootTimeoutId=void 0,document.querySelector("#sphereImage").style.filter="none",peers.push(...await fetchPeers()),makePeers(document.getElementById("room_container"),config.peers,!0),showSlideIndex(lastSplideIndex)}async function onOffline(){console.log("onOffline"),onWebSocketConnected(!1),showSlideID("landing"),document.querySelector("#sphereImage").style.filter="invert(30%)",allowInteraction(!1),targetWarmth=0}async function getConfiguration(e=5e3,t=5){for(let n=0;n<t;n++){try{const t=await fetchWithTimeout("/yoyo/config",e);if(t.ok){const e=await t.json();if(e.statuscode){let t=Number(e.statuscode);t=webSocketConnected?8|t:t,onStatus(t),delete e.statuscode}return updateCount(),setConfiguration(e,!1),!0}onStatus(0)}catch(e){onStatus(0)}await new Promise(e=>setTimeout(e,1e3))}return!1}function isPlainObject(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function isEmptyObject(e){return isPlainObject(e)&&0===Object.keys(e).length}async function setConfiguration(e,t=!0,n=-1){let o=!1;return isPlainObject(config)&&!isEmptyObject(e)?(console.log("setConfiguration",e),config={...config,...e},console.log(JSON.stringify(config)),t&&(o=postJson("/yoyo/config",config,1e3)),o&&t&&n>=0&&(onSphereDown=void 0,rebootTimeoutId=setTimeout(function(){sphereIsUp()?onSphereDown=function(){drawSphere(statuscode),reboot()}:reboot()},n),showSlideID("landing"),allowInteraction(!1))):o=!1,o}function onStart(){console.log("onStart",config);const e=function(){showCarousel(!0)};config?.server?.host?config?.mic?.frequency?!config?.wifi?.ssid||captivePortalRunning()?showSlideID("wifi",e):showSlideID("landing",e):showSlideID("tuning",e):showSlideID("server",e)}async function onSlideMoved(){console.log("onSlideMoved")}async function activateTuning(e=!0){if(e&&!tuningState.running)setMic({level:mic?.level??1,frequency:-1,bandwidth:-1,rate:5},!1),tuningState.timeOutId=void 0,tuningState.goodPeakFound=!1,tuningState.running=!0;else if(!e&&tuningState.running){void 0!==tuningState.timeOutId&&(clearTimeout(tuningState.timeOutId),tuningState.timeOutId=void 0);setMic({rate:-1,frequency:tuningState.goodPeakFound?mic.frequency:config?.mic?.frequency,bandwidth:20},!0),tuningState.goodPeakFound=!1,tuningState.running=!1}}function drawEllipse(e,t,n){const o=e.getContext("2d");o.beginPath(),o.ellipse(e.width/2,e.height/2,t/2,n/2,0,0,2*Math.PI),o.strokeStyle="gray",o.lineWidth=2,o.stroke()}function drawEllipseWithImages(e,t,n){const o=e.getContext("2d"),i=e.width/2,r=e.height/2,s=t/2,c=n/2,a=new Image;a.src="img/sphere-up.png",a.onload=function(){const e=2*Math.PI/3;for(let t=0;t<3;t++){const n=t*e,l=i+s*Math.cos(n),u=r+c*Math.sin(n);o.drawImage(a,l-25,u-25,50,50)}}}function makePeers(e,t,n=!1){if(e&&t){const n=Object.keys(t);for(let o=0;o<n.length;o++){const i=n[o];document.getElementById(i)||makePeer(i,t[i].user,e)}}n&&layoutPeers(e)}function layoutPeers(e){if(e){const t=e.clientWidth/2,n=e.clientHeight/2,o=e.clientWidth/2,i=e.clientHeight/2,r=Array.from(e.children);for(let e=0;e<r.length;e++){const s=2*e*Math.PI/r.length,c=t+o*Math.cos(s),a=n+i*Math.sin(s);let l=r[e];l&&(l.style.left=`${c}px`,l.style.top=`${a}px`)}}}function makePeer(e,t,n){let o=document.getElementById("room_item_template").content.cloneNode(!0).firstElementChild;return o.id=e,t&&(o.querySelector("span").textContent=t),updatePeer(o,!1),o.addEventListener("mousedown",()=>{onUserClicked({id:o.id})}),o.addEventListener("mouseup",()=>{onUserClicked({id:o.id},!1)}),o.addEventListener("touchstart",()=>{onUserClicked({id:o.id})}),o.addEventListener("touchend",()=>{onUserClicked({id:o.id},!1)}),n&&n.appendChild(o),o}function onUserClicked(e,t=!0){e={...e,amplitude:.5,duration:t?1e4:100,fade:100},console.log("onUserClicked",e),postJson("/yoyo/tone",e,500)}function updatePeer(e,t){if(e){peerTimeOutId[e.id]&&clearTimeout(peerTimeOutId[e.id]);const n=e.querySelector("img");t?(n.src="img/sphere-up.png",peerTimeOutId[e.id]=setTimeout(()=>{updatePeer(e,!1)},1e4)):n.src="img/sphere-down.png"}}function isStandalone(){return"standalone"===getDisplayMode()}function getDisplayMode(){const e=["fullscreen","standalone","minimal-ui","browser"];for(const t of e)if(window.matchMedia(`(display-mode: ${t})`).matches)return t;return"unknown"}function getUserAgent(){return navigator.userAgent||navigator.vendor||window.opera}function getDeviceType(){const e=getUserAgent();return/iPhone|Android.*Mobile|Windows Phone|BlackBerry|webOS/i.test(e)?"phone":"computer"}function getOS(){let e="unknown";const t=getUserAgent();return/Android/i.test(t)?e="android":/Windows NT/i.test(t)?e="windows":/iPhone|iPad|iPod/i.test(t)?e="ios":/Macintosh|Mac OS X/i.test(t)&&(e="macos"),e}function getBrowser(){let e="unknown";const t=getUserAgent();return/crios|chrome/i.test(t)?e="chrome":/safari/i.test(t)&&(e="safari"),e}function generateLandingText(){const e=config?.wifi?.ssid??"";let t="";return sphereWillReboot()?(sphereIsUp()?t+="Turn the sphere over now and it ":t+="The sphere",t+=" will try to connect to the <span class='ssid'>"+e+"</span> WiFi network. "):sphereIsOnline()?captivePortalRunning()?(t+="Your sphere needs to be "+(config?.mic?.frequency&&config?.server?.host?"":"configured and ")+"connected to a WiFi network",t+=""!==e?", it couldn't connect to <span class='ssid'>"+e+"</span>. ":". ",localConnected()&&!sphereIsUp()&&(t+="To get started, please turn the sphere over. ",onSphereUp=function(){onStart()})):(t+="Your sphere is connect"+(localConnected()?"ed":"ing")+" to a WiFi network",localConnected()?remoteConnected()?(t+=" and a Resound server. ",t+=" Everything looks good. "):t+=" but not a Resound server. ":t+=".<br>Please wait. "):(t+="Your sphere appears to be offline. ",""!==e&&(t+="It was last connected to the <span class='ssid'>"+e+"</span> WiFi network. Is the sphere plugged in? Is this "+getDeviceType()+" on that network too?")),t.trim()}function generateWiFiText(e){const t=config?.wifi?.ssid??"";let n="Your sphere is ";return!captivePortalRunning()&&sphereIsOnline()?n+="connected to the <span class='ssid'>"+t+"</span> WiFi network ("+getHost()+"). ":(n+="not connected to a WiFi network",n+=""!==t?", it couldn't connect to <span class='ssid'>"+t+"</span>. ":". ",e&&(n+="Select a network, enter the password and press connect. ")),n.trim()}function generateServerText(){let e="Your sphere is ";return captivePortalRunning()?e+="not connected to the Internet. ":remoteConnected()?e+="connected to a Resound server ("+(config?.server?.host??"")+"). ":e+="not connected to a Resound server. ",e.trim()}function generateTuningText(){let e="";const t=config?.mic?.frequency,n=void 0!==t;return e+=n?"Your sphere is tuned to a frequency of "+t+"Hz"+(getNoteName(t)?" (the note of "+getNoteName(t)+")":"")+".<br>":"Your sphere isn't tuned.<br>",sphereIsUp()?e+="Chant NMRK to "+(n?"re":"")+"tune it. ":e+="To get started, please turn the sphere over. ",e.trim()}function allowInteraction(e){showCarouselControls(e),allowSwipe(e)}function allowSwipe(e){if(console.log("allowSwipe",e,splide.options.drag,true),(e=e&&true)!==splide.options.drag){const t=document.querySelector(".splide__arrows"),n=document.querySelector(".splide__pagination"),o="block"===t.style.display&&"flex"===n.style.display;splide.options={drag:e},splide.refresh(),showCarouselControls(o)}}function showCarousel(e){var t=document.getElementById("carousel");t.style.visibility=e?"visible":"hidden",t.style["pointer-events"]=e?"auto":"none"}function showCarouselControls(e){const t=document.querySelector(".splide__arrows"),n=document.querySelector(".splide__pagination");t&&(t.style.display=e?"block":"none"),n&&(n.style.display=e?"flex":"none")}function updateCount(e=0){document.getElementById("count").textContent=Math.max(config?.server?.room?.count??0,e)}async function updateSlide(e=!1){console.log("updateSlide()",e,lastSplideIndex,splide.index),onSphereDown=void 0,onSphereUp=void 0;const t=getSlideId();activateTuning("tuning"===t&&sphereIsUp());const n=document.getElementById("room_container");n.style.display=sphereIsOnline()?"block":"none";const o=getSlideByID(t).querySelectorAll(".slide-content .row")[2];switch(t){case"landing":layoutPeers(n),o&&(o.querySelector("span").innerHTML=generateLandingText()),allowInteraction(webSocketConnected);break;case"tuning":onSphereDown=function(){updateSlide(),console.log("TODO: tuning - onSphereDown")},onSphereUp=function(){updateSlide(),console.log("TODO: tuning - onSphereUp")},o&&(o.querySelector("span").innerHTML=generateTuningText());break;case"server":const i=document.getElementById("server_name"),r=document.getElementById("server_host"),s=document.getElementById("server_channel");i.value=config?.server?.name??"",r.value=config?.server?.host??"",s.value=config?.server?.room?.channel??"",o&&(o.innerHTML=generateServerText());break;case"wifi":e&&fetchWiFiNetworks().then(e=>{populateWiFiForm(config,e),o&&(o.innerHTML=generateWiFiText(e.length>0))}),allowInteraction(webSocketConnected);break;case"determination":document.getElementById("determination_individual").value=config?.server?.room?.determination??"";break;case"volume":const c=document.getElementById("vollevel");c.disabled=!sphereIsUp(),c.onchange=function(){onVolumeChanged(c.value/100)},onVolumeChanged(config?.volume??1,!1),onSphereDown=function(){updateSlide(),console.log("TODO: volume - onSphereDown")},onSphereUp=function(){updateSlide(),console.log("TODO: volume - onSphereUp")},o&&(o.querySelector("span").innerHTML=sphereIsUp()?o.querySelector(".sphere_up_text").innerHTML:o.querySelector(".sphere_down_text").innerHTML);break;default:console.log("no rule for: "+t)}}function getNoteName(e){let t;if(e>0){const n=440;t=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"][(Math.round(12*Math.log2(e/n))+9+12)%12]}return t}function mute(e=!0){}function onVolumeChanged(e,t=!0){console.log("vollevel",e,t),config.volume=e,t?(postJson("/yoyo/volume",{v:config.volume},500),console.log("localchange vollevel",e),saveConfigTimeOutId&&clearTimeout(saveConfigTimeOutId),saveConfigTimeOutId=setTimeout(function(){postJson("/yoyo/config",{},500),saveConfigTimeOutId=void 0},3e3)):vollevel.value=100*e}function isWarm(){return warmth>4.25}function isCold(){return warmth<.75}function showNextSlide(){splide.go(">")}function showSlideID(e,t=void 0){showSlideIndex(getSlideIndexByID(e),t)}function showSlideIndex(e,t=void 0){splide&&e>=0&&(e!=splide.index?(onMovedOneTime=t,splide.go(e)):t&&t())}function getSlideByID(e){const t=document.querySelector(".splide__track").querySelectorAll(".splide__slide");for(let n of t)if(n.getAttribute("data-id")===e)return n;return null}function getSlideIndexByID(e){const t=document.querySelector(".splide__track").querySelectorAll(".splide__slide");let n=-1;return t.forEach((t,o)=>{t.getAttribute("data-id")===e&&(n=o)}),n}function getSlideId(){let e=null;try{e=getSlideIdByIndex(splide.index)}catch(e){}return e}function getSlideIdByIndex(e){const t=document.querySelector(".splide__track").querySelectorAll(".splide__slide");return e>=0&&e<t.length?t[e].getAttribute("data-id"):null}async function postJson(e,t={},n=5e3){let o=!1;if(e)try{let i={method:"POST",headers:{Accept:"application/json; charset=utf-8","Content-Type":"application/json"}};i.body=JSON.stringify(t);const r=await fetchWithTimeout(e,n,i);r.ok&&(console.log(r),o=!0)}catch(e){console.log(e)}return o}async function setMic(e,t=!1){if(Object.assign(mic,e),mic.frequency&&mic.bandwidth){if(t&&(config.mic=config.mic??{},config.mic.frequency=mic?.frequency,config.mic.bandwidth=mic?.bandwidth,config.mic.level=mic?.level),micPostLocked)return;postJson("/yoyo/mic",{...mic,save:t},1500)}console.log("setMic",mic,t)}async function setSound(e){postJson("/yoyo/sound",e,500)}function redirect(e){location.href=e}async function fetchWiFiNetworks(){try{let e=await fetchWithTimeout("/yoyo/networks",1e3);if(!e.ok)throw new Error("Failed to fetch networks");const t=await e.json();return Array.isArray(t)&&0!==t.length?Object.values(t.reduce((e,t)=>((!e[t.SSID]||t.RSSI>e[t.SSID].RSSI)&&(e[t.SSID]=t),e),{})).map(e=>e.SSID):[]}catch(e){return console.error("Error fetching WiFi networks:",e),[]}}function populateWiFiForm(e,t=[]){let n=document.getElementById("wifi_ssid");n.innerHTML="";const o=e?.wifi?.ssid??"";if(t.forEach(t=>{let i=document.createElement("option");i.value=t,i.textContent=t,n.appendChild(i),t===o&&(i.selected=!0,document.getElementById("wifi_secret").value=e?.wifi?.secret??"")}),o&&!t.includes(o)){let e=document.createElement("option");e.value=o,e.textContent=o,e.disabled=!0,n.insertBefore(e,n.firstChild)}if(0===n.options.length){let e=document.createElement("option");e.textContent="No Networks Found",e.disabled=!0,n.appendChild(e)}else n.removeAttribute("disabled"),document.getElementById("wifi_secret").removeAttribute("disabled"),document.getElementById("wifi_button").removeAttribute("disabled")}async function onWiFiSaveEvent(e){console.log("onWiFiSaveEvent",e);const t=e.get("ssid"),n=e.get("secret");t&&t.length>0&&setConfiguration({wifi:{ssid:t,secret:n}},!0,1e3)}async function onServerSaveEvent(e){console.log("onServerSaveEvent",e),setConfiguration({server:{name:e.get("name"),host:e.get("host"),room:{channel:e.get("channel")}}})}async function fetchPeers(){try{let e=await fetchWithTimeout("/yoyo/peers",1500);if(!e.ok)throw new Error("Failed to fetch networks");const t=await e.json();return Array.isArray(t)&&0!==t.length?t:[]}catch(e){return console.error("Error fetching peers:",e),[]}}function webSocketConnect(){let e="ws://"+getHost()+":81/";console.log("Attempting to open "+e),webSocket=new WebSocket(e),webSocket.onopen=function(){console.debug("webSocket connected"),onWebSocketConnected()},webSocket.onmessage=async function(e){onWebSocketConnected(!0),parseLocalMessage(JSON.parse(e.data))},webSocket.onclose=function(e){onWebSocketConnected(!1)},webSocket.onerror=function(e){console.debug("webSocket.onerror",e)}}function manageWebSocket(e=null){onWebSocketConnectedOneTime=e,webSocketReconnect||(webSocketReconnect=function(){!webSocketConnected&&sphereIsOnline()&&webSocketConnect()},webSocketReconnect(),setInterval(webSocketReconnect,1e4))}function parseLocalMessage(e){if(e){if(e.status){let t=e.status;t=webSocketConnected?8|t:t,t!==statuscode&&onStatus(t)}"debug"===e.type?parseLocalDebugMessage(e):"peer"===e.type?parseLocalPeerMessage(e):"sound"===e.type?parseLocalSoundMessage(e):"touch"===e.type?parseLocalTouchMessage(e):"gesture"===e.type?parseLocalGestureMessage(e):"volume"===e.type?parseLocalVolumeMessage(e):"status"===e.type||parseLocalDebugMessage(e)}}function parseLocalDebugMessage(e){console.log("debug",e)}function parseLocalPeerMessage(e){if(e.id){const t=peers.indexOf(e.id);e.arrived&&-1===t?peers.push(e.id):!e.arrived&&t>=0&&peers.splice(t,1);let n=document.getElementById(e.id);if(!n){const t=document.getElementById("room_container");n=makePeer(e.id,void 0,t),layoutPeers(t)}updatePeer(n,e.arrived)}}function addPeerConsoleText(e){const t=document.getElementById("peerconsole");t&&(t.value+=e,t.scrollTop=t.scrollHeight)}function parseLocalSoundMessage(e){tuningState.timeOutId&&console.log("parseLocalSoundMessage"+JSON.stringify(e));const t=e.f;micMinFreq=e.fl,micMaxFreq=e.fh;const n=e.v,o=e.e;targetWarmth=o,tuningState.timeOutId&&addSampleToHistogram(t,n),peakEnergy=tuningState.timeOutId?Math.max(o,peakEnergy):0}function parseLocalTouchMessage(e){console.log("touch",e),onTouchOneTime&&(onTouchOneTime(),onTouchOneTime=null)}function parseLocalGestureMessage(e){console.log("gesture",e);const t=e.t;"clk"!==t&&"anti"!==t||showSlideID("volume")}function parseLocalVolumeMessage(e){console.log("volume",e),onVolumeChanged(e.v,!1)}function getHost(){let e=window.location.host;if(e.length>0){let t=e.indexOf(":");t>=0&&(e=e.slice(0,t));let n=e.split(".");4==n.length&&NaN!=parseInt(n[0])||(e="192.168.4.1")}else e="192.168.4.1";return e}function setBackgroundFromValue(e){const t=(e,t,n)=>e+(t-e)*n;var n,o,i;n=e,o=0,i=255,e=Math.min(Math.max(n,o),i)/255,e*=e;const r=245,s=245,c=245,a=255,l=215,u=0,d=Math.round(t(r,a,e)),g=Math.round(t(s,l,e)),m=Math.round(t(c,u,e));document.body.style.backgroundColor=`rgb(${d}, ${g}, ${m})`}function addSampleToHistogram(e,t){const n=(micMaxFreq-micMinFreq)/histogram.length,o=Math.floor((e-micMinFreq)/n);o>=0&&o<histogram.length&&(histogram[o]+=t)}function clearHistogram(e){for(let t=0;t<e.length;t++)e[t]=0}function findHistogramPeak(e){if(0===e.length)return{value:null,position:-1};let t=Math.max(...e);return{value:t,position:e.indexOf(t)}}function calculateHistogramStats(e){if(0===e.length)return{mean:null,sd:null};const t=e.reduce((e,t)=>e+t,0)/e.length,n=e.reduce((e,n)=>e+Math.pow(n-t,2),0)/e.length,o=Math.sqrt(n);return{mean:t,sd:o}}function getGoodHistogramPeak(e,t){let n=-1,o=-1;if(e.length>0){const{mean:i,sd:r}=calculateHistogramStats(e),s=findHistogramPeak(e);if(console.log("---histogram stats---","mean:",i,"peak:",s,"threshold:",t),s.value>t&&s.value>i+2*r){console.log("---peak.value---",s.value);const t=(micMaxFreq-micMinFreq)/e.length;n=Math.floor(micMinFreq+s.position*t+t/2),o=s.value}}return{frequency:n,value:o}}function playTone(e,t,n){if(void 0===audioCtx)return;const o=t/1e3;"suspended"===audioCtx.state&&audioCtx.resume();const i=audioCtx.createOscillator();i.type="sine",i.frequency.setValueAtTime(e,audioCtx.currentTime);const r=audioCtx.createGain();i.connect(r),r.connect(audioCtx.destination);const s=.2*o,c=audioCtx.currentTime,a=c+o;r.gain.setValueAtTime(0,c),r.gain.linearRampToValueAtTime(n,c+s),r.gain.setValueAtTime(n,a-s),r.gain.linearRampToValueAtTime(0,a),i.start(c),i.stop(a)}async function registerServiceWorker(){if("serviceWorker"in navigator)try{const e=await navigator.serviceWorker.register("./service-worker.js");console.log("service worker registered",e)}catch(e){console.error("service worker registration failed",e)}}setTimeout(()=>{micPostLocked=!1,console.log("MIC POSTS UNLOCKED")},5e3),window.addEventListener("load",function(){console.log("Static assets loaded. Waiting for network settle..."),setTimeout(()=>{console.log("Starting API initialization..."),init()},1500)});