//page js var loc = false, locip; var noNewSegs = false; var isOn = false, isInfo = false, isNodes = false, isRgbw = false, cct = false; var whites = [0,0,0]; var selColors; var powered = [true]; var selectedFx = 0; var selectedPal = 0; var csel = 0; var currentPreset = -1; var lastUpdate = 0; var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0; var tr = 7; var d = document; var palettesData; var fxdata = []; var pJson = {}, eJson = {}, lJson = {}; var pN = "", pI = 0, pNum = 0; var pmt = 1, pmtLS = 0, pmtLast = 0; var lastinfo = {}; var ws, cpick, ranges; var cfg = { theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}}, comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, labels:true, pcmbot:false, pid:true, seglen:false} }; var hol = [ [0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas [0,2,17,1,"https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day [2022,3,17,2,"https://aircoookie.github.io/easter.png"], [2023,3,9,2,"https://aircoookie.github.io/easter.png"], [2024,2,31,2,"https://aircoookie.github.io/easter.png"] ]; function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();} function sCol(na, col) {d.documentElement.style.setProperty(na, col);} function gId(c) {return d.getElementById(c);} function gEBCN(c) {return d.getElementsByClassName(c);} function isEmpty(o) {return Object.keys(o).length === 0;} function isObj(i) { return (i && typeof i === 'object' && !Array.isArray(i)); } function applyCfg() { cTheme(cfg.theme.base === "light"); var bg = cfg.theme.color.bg; if (bg) sCol('--c-1', bg); var ccfg = cfg.comp.colors; //gId('picker').style.display = "none"; // ccfg.picker ? "block":"none"; //gId('vwrap').style.display = "none"; // ccfg.picker ? "block":"none"; //gId('rgbwrap').style.display = ccfg.rgb ? "block":"none"; gId('qcs-w').style.display = ccfg.quick ? "block":"none"; var l = cfg.comp.labels; //l = false; var e = d.querySelectorAll('.tab-label'); for (var i=0; i { var a = parseFloat(cfg.theme.alpha.bg); if (isNaN(a)) a = 0.6; bg.style.opacity = a; bg.style.backgroundImage = `url(${img.src})`; img = null; }); } function loadSkinCSS(cId) { if (!gId(cId)) // check if element exists { var h = document.getElementsByTagName('head')[0]; var l = document.createElement('link'); l.id = cId; l.rel = 'stylesheet'; l.type = 'text/css'; l.href = (loc?`http://${locip}`:'.') + '/skin.css'; l.media = 'all'; h.appendChild(l); } } async function onLoad() { if (window.location.protocol == "file:") { loc = true; locip = localStorage.getItem('locIp'); if (!locip) { locip = prompt("File Mode. Please enter WLED IP!"); localStorage.setItem('locIp', locip); } } var sett = localStorage.getItem('wledUiCfg'); if (sett) cfg = mergeDeep(cfg, JSON.parse(sett)); makeWS(); applyCfg(); if (cfg.theme.bg.url=="" || cfg.theme.bg.url === "https://picsum.photos/1920/1080") { var iUrl = cfg.theme.bg.url; fetch((loc?`http://${locip}`:'.') + "/holidays.json", { method: 'get' }) .then((res)=>{ return res.json(); }) .then((json)=>{ if (Array.isArray(json)) hol = json; //TODO: do some parsing first }) .catch((e)=>{ console.log("holidays.json does not contain array of holidays. Defaults loaded."); }) .finally(()=>{ var today = new Date(); for (var i=0; i=hs && today{ setColor(1); }); pmtLS = localStorage.getItem('wledPmt'); // Load initial data loadPalettes(()=>{ loadPalettesData(redrawPalPrev); loadFX(()=>{ loadFXData(); loadPresets(()=>{ requestJson(); }); }); }); d.addEventListener("visibilitychange", handleVisibilityChange, false); size(); gId("cv").style.opacity=0; var sls = d.querySelectorAll('input[type="range"]'); for (var sl of sls) { sl.addEventListener('touchstart', toggleBubble); sl.addEventListener('touchend', toggleBubble); } } var timeout; function showToast(text, error = false) { if (error) gId('connind').style.backgroundColor = "var(--c-r)"; var x = gId("toast"); x.innerHTML = text; x.className = error ? "error":"show"; clearTimeout(timeout); x.style.animation = 'none'; timeout = setTimeout(()=>{ x.classList.remove("show"); }, 2900); if (error) console.log(text); } function showErrorToast() { if (ws && ws.readyState === WebSocket.OPEN) { // if we received a timeout force WS reconnect ws.close(); ws = null; if (lastinfo.ws > -1) setTimeout(makeWS,500); } showToast('Connection to light failed!', true); } function clearErrorToast() {gId("toast").className = gId("toast").className.replace("error", "");} function getRuntimeStr(rt) { var t = parseInt(rt); var days = Math.floor(t/86400); var hrs = Math.floor((t - days*86400)/3600); var mins = Math.floor((t - days*86400 - hrs*3600)/60); var str = days ? (days + " " + (days == 1 ? "day" : "days") + ", ") : ""; str += (hrs || days) ? (hrs + " " + (hrs == 1 ? "hour" : "hours")) : ""; if (!days && hrs) str += ", "; if (t > 59 && !days) str += mins + " min"; if (t < 3600 && t > 59) str += ", "; if (t < 3600) str += (t - mins*60) + " sec"; return str; } function inforow(key, val, unit = "") { return `${key}${val}${unit}`; } function pName(i) { var n = "Preset " + i; if (pJson && pJson[i] && pJson[i].n) n = pJson[i].n; return n; } function isPlaylist(i) { return pJson[i].playlist && pJson[i].playlist.ps; } function papiVal(i) { if (!pJson || !pJson[i]) return ""; var o = Object.assign({},pJson[i]); if (o.win) return o.win; delete o.n; delete o.p; delete o.ql; return JSON.stringify(o); } function qlName(i) { if (!pJson || !pJson[i] || !pJson[i].ql) return ""; return pJson[i].ql; } function cpBck() { var copyText = gId("bck"); copyText.select(); copyText.setSelectionRange(0, 999999); d.execCommand("copy"); showToast("Copied to clipboard!"); } function loadPresets(callback = null) { //1st boot (because there is a callback) if (callback && pmt == pmtLS && pmt > 0) { //we have a copy of the presets in local storage and don't need to fetch another one pJson = JSON.parse(localStorage.getItem("wledP")); populatePresets(); pmtLast = pmt; callback(); return; } //afterwards if (!callback && pmt == pmtLast) return; pmtLast = pmt; var url = (loc?`http://${locip}`:'') + '/presets.json'; fetch(url, { method: 'get' }) .then(res => { if (!res.ok) showErrorToast(); return res.json(); }) .then(json => { clearErrorToast(); pJson = json; populatePresets(); }) .catch(function (error) { showToast(error, true); console.log(error); }) .finally(()=>{ if (callback) setTimeout(callback,99); }); } function loadPalettes(callback = null) { var url = (loc?`http://${locip}`:'') + '/json/palettes'; fetch(url, { method: 'get' }) .then(res => { if (!res.ok) showErrorToast(); return res.json(); }) .then(json => { clearErrorToast(); lJson = Object.entries(json); populatePalettes(); }) .catch(function (error) { showToast(error, true); }) .finally(()=>{ if (callback) callback(); }); } function loadFX(callback = null) { var url = (loc?`http://${locip}`:'') + '/json/effects'; fetch(url, { method: 'get' }) .then(res => { if (!res.ok) showErrorToast(); return res.json(); }) .then(json => { clearErrorToast(); eJson = Object.entries(json); populateEffects(); }) .catch(function (error) { showToast(error, true); }) .finally(()=>{ if (callback) callback(); }); } function loadFXData(callback = null) { var url = (loc?`http://${locip}`:'') + '/json/fxdata'; fetch(url, { method: 'get' }) .then(res => { if (!res.ok) showErrorToast(); return res.json(); }) .then(json => { clearErrorToast(); fxdata = json||[]; // add default value for Solid fxdata.shift() fxdata.unshift("@;!;"); }) .catch(function (error) { fxdata = []; showToast(error, true); }) .finally(()=>{ if (callback) callback(); updateUI(); }); } var pQL = []; function populateQL() { var cn = ""; if (pQL.length > 0) { pQL.sort((a,b) => (a[0]>b[0])); for (var key of (pQL||[])) { cn += ``; } } gId('pql').innerHTML = cn; } function populatePresets() { if (!pJson) {pJson={};return}; delete pJson["0"]; var cn = ""; //`

All presets

`; var arr = Object.entries(pJson); arr.sort(cmpP); pQL = []; var is = []; pNum = 0; for (var key of (arr||[])) { if (!isObj(key[1])) continue; let i = parseInt(key[0]); var qll = key[1].ql; if (qll) pQL.push([i, qll, pName(i)]); is.push(i); cn += `
`; //if (cfg.comp.pid) cn += `
${i}
`; cn += `${isPlaylist(i)?"":""}${pName(i)}
`; pNum++; } gId('pcont').innerHTML = cn; updatePA(); populateQL(); } function parseInfo() { var li = lastinfo; var name = li.name; gId('namelabel').innerHTML = name; // if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)"; if (li.live) name = "(Live) " + name; if (loc) name = "(L) " + name; d.title = name; isRgbw = li.leds.wv; ledCount = li.leds.count; syncTglRecv = li.str; maxSeg = li.leds.maxseg; pmt = li.fs.pmt; cct = li.leds.cct; } function populateInfo(i) { var cn=""; var heap = i.freeheap/1000; heap = heap.toFixed(1); var pwr = i.leds.pwr; var pwru = "Not calculated"; if (pwr > 1000) {pwr /= 1000; pwr = pwr.toFixed((pwr > 10) ? 0 : 1); pwru = pwr + " A";} else if (pwr > 0) {pwr = 50 * Math.round(pwr/50); pwru = pwr + " mA";} var urows=""; if (i.u) { for (const [k, val] of Object.entries(i.u)) { if (val[1]) urows += inforow(k,val[0],val[1]); else urows += inforow(k,val); } } var vcn = "Kuuhaku"; if (i.ver.startsWith("0.13.")) vcn = "Toki"; if (i.ver.includes("-bl")) vcn = "Ryujin"; if (i.cn) vcn = i.cn; cn += `v${i.ver} "${vcn}"

${urows} ${inforow("Build",i.vid)} ${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} ${inforow("Uptime",getRuntimeStr(i.uptime))} ${inforow("Free heap",heap," kB")} ${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""} ${inforow("Estimated current",pwru)} ${inforow("Average FPS",i.leds.fps)} ${inforow("MAC address",i.mac)} ${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")} ${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")}
`; gId('kv').innerHTML = cn; } function populateSegments(s) { var cn = ""; segCount = (s.seg||[]).length; lowestUnused = 0; lSeg = 0; if (segCount > 1) { for (var y = 0; y < segCount && y<4; y++) { var inst=s.seg[y]; let i = parseInt(inst.id); powered[i] = inst.on; if (i == lowestUnused) lowestUnused = i+1; if (i > lSeg) lSeg = i; cn += `
${(inst.n&&inst.n!=='')?inst.n:('Segment '+y)}
`; } //if (gId('buttonBri').className !== 'active') tglBri(true); } else { //tglBri(false); } //gId('buttonBri').style.display = (segCount > 1) ? "block" : "none"; gId('segcont').innerHTML = cn; for (var i = 0; i < segCount && i<4; i++) updateTrail(gId(`seg${i}bri`)); } function btype(b) { switch (b) { case 2: case 32: return "ESP32"; case 1: case 82: return "ESP8266"; } return "?"; } function bname(o) { if (o.name=="WLED") return o.ip; return o.name; } function populateNodes(i,n) { var cn=""; var urows=""; var nnodes = 0; if (n.nodes) { n.nodes.sort((a,b) => (a.name).localeCompare(b.name)); for (var x=0;x${bname(o)}`; urows += inforow(url,`${btype(o.type)}
${o.vid==0?"N/A":o.vid}`); nnodes++; } } } if (i.ndc < 0) cn += `Instance List is disabled.`; else if (nnodes == 0) cn += `No other instances found.`; cn += ` ${urows} ${inforow("Current instance:",i.name)}
`; gId('kn').innerHTML = cn; } function loadNodes() { var url = (loc?`http://${locip}`:'') + '/json/nodes'; fetch(url, { method: 'get' }) .then(res => { if (!res.ok) showToast('Could not load Node list!', true); return res.json(); }) .then(json => { clearErrorToast(); populateNodes(lastinfo, json); }) .catch(function (error) { showToast(error, true); console.log(error); }); } function populateEffects() { var effects = eJson; var html = ""; effects.shift(); //remove solid for (let i = 0; i < effects.length; i++) effects[i] = {id: effects[i][0], name:effects[i][1]}; effects.sort((a,b) => (a.name).localeCompare(b.name)); effects.unshift({ "id": 0, "name": "Solid@;!;0" }); for (let i = 0; i < effects.length; i++) { // WLEDSR: add slider and color control to setEffect (used by requestjson) if (effects[i].name.indexOf("RSVD") < 0) { var posAt = effects[i].name.indexOf("@"); var extra = ''; if (posAt > 0) extra = effects[i].name.substr(posAt); else posAt = 999; html += generateListItemHtml( 'fx', effects[i].id, effects[i].name.substr(0,posAt), 'setEffect', '','', extra ); } } gId('fxlist').innerHTML=html; } function populatePalettes() { var palettes = lJson; palettes.shift(); //remove default for (let i = 0; i < palettes.length; i++) { palettes[i] = { "id": palettes[i][0], "name": palettes[i][1] }; } palettes.sort((a,b) => (a.name).localeCompare(b.name)); palettes.unshift({ "id": 0, "name": "Default", }); var html = ""; for (let i = 0; i < palettes.length; i++) { html += generateListItemHtml( 'palette', palettes[i].id, palettes[i].name, 'setPalette', `
` ); } gId('pallist').innerHTML=html; } function redrawPalPrev() { let palettes = d.querySelectorAll('#pallist .lstI'); for (let i = 0; i < palettes.length; i++) { let id = palettes[i].dataset.id; let lstPrev = palettes[i].querySelector('.lstIprev'); if (lstPrev) { lstPrev.style = genPalPrevCss(id); } } } function genPalPrevCss(id) { if (!palettesData) return; var paletteData = palettesData[id]; var previewCss = ""; if (!paletteData) return 'display: none'; // We need at least two colors for a gradient if (paletteData.length == 1) { paletteData[1] = paletteData[0]; if (Array.isArray(paletteData[1])) { paletteData[1][0] = 255; } } var gradient = []; for (let j = 0; j < paletteData.length; j++) { const element = paletteData[j]; let r; let g; let b; let index = false; if (Array.isArray(element)) { index = element[0]/255*100; r = element[1]; g = element[2]; b = element[3]; } else if (element == 'r') { r = Math.random() * 255; g = Math.random() * 255; b = Math.random() * 255; } else { if (selColors) { let e = element[1] - 1; r = selColors[e][0]; g = selColors[e][1]; b = selColors[e][2]; } } if (index === false) { index = j / paletteData.length * 100; } gradient.push(`rgb(${r},${g},${b}) ${index}%`); } return `background: linear-gradient(to right,${gradient.join()});`; } function generateOptionItemHtml(id, name) { return ``; } function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', extraClass = '', extraPar = '') { return `
${name}
${extraHtml}
`; } //update the 'sliderdisplay' background div of a slider for a visual indication of slider position function updateTrail(e) { if (e==null) return; var max = e.hasAttribute('max') ? e.attributes.max.value : 255; var perc = e.value * 100 / max; perc = parseInt(perc); if (perc < 50) perc += 2; var val = `linear-gradient(90deg, var(--c-f) ${perc}%, var(--c-4) ${perc}%)`; e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val; var b = e.parentNode.parentNode.getElementsByTagName('output')[0]; if (b) b.innerHTML = e.value; } //rangetouch slider function function toggleBubble(e) { var b = e.target.parentNode.parentNode.getElementsByTagName('output')[0]; b.classList.toggle('sliderbubbleshow'); } function updatePA() { var ps = gEBCN("pres"); for (let i = 0; i < ps.length; i++) { ps[i].classList.remove('selected');; } ps = gEBCN("psts"); for (let i = 0; i < ps.length; i++) { ps[i].classList.remove('selected');; } if (currentPreset > 0) { var acv = gId(`p${currentPreset}o`); if (acv) acv.classList.add('selected'); acv = gId(`p${currentPreset}qlb`); if (acv) acv.classList.add('selected'); } } function updateUI() { gId('buttonPower').className = (isOn) ? "active":""; var sel = 0; if (lJson && lJson.length) { for (var i=0; i b[0]); // playlists follow presets var name = (a[1].playlist ? '~' : ' ') + a[1].n; return name.localeCompare((b[1].playlist ? '~' : ' ') + b[1].n, undefined, {numeric: true}); } function makeWS() { if (ws) return; ws = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws'); ws.onmessage = (e)=>{ var json = JSON.parse(e.data); if (json.leds) return; //liveview packet clearTimeout(jsonTimeout); jsonTimeout = null; lastUpdate = new Date(); clearErrorToast(); gId('connind').style.backgroundColor = "var(--c-l)"; // json object should contain json.info AND json.state (but may not) var i = json.info; if (i) { lastinfo = i; parseInfo(); if (isInfo) populateInfo(i); } else i = lastinfo; var s = json.state ? json.state : json; readState(s); }; ws.onclose = (e)=>{ gId('connind').style.backgroundColor = "var(--c-r)"; ws = null; if (lastinfo.ws > -1) setTimeout(makeWS,500); } ws.onopen = (e)=>{ ws.send("{'v':true}"); reqsLegal = true; clearErrorToast(); } } function readState(s,command=false) { if (!s) return false; isOn = s.on; gId('sliderBri').value= s.bri; nlA = s.nl.on; nlDur = s.nl.dur; nlTar = s.nl.tbri; nlFade = s.nl.fade; syncSend = s.udpn.send; if (s.pl<0) currentPreset = s.ps; else currentPreset = s.pl; tr = s.transition/10; var selc=0; var ind=0; populateSegments(s); for (let i = 0; i < (s.seg||[]).length; i++) { if(s.seg[i].sel) {selc = ind; break;} ind++; } var i=s.seg[selc]; if (!i) { showToast('No Segments!', true); updateUI(); return; } selColors = i.col; var cd = gId('csl').children; for (let e = cd.length-1; e >= 0; e--) { var r,g,b,w; r = i.col[e][0]; g = i.col[e][1]; b = i.col[e][2]; if (isRgbw) w = i.col[e][3]; cd[e].style.backgroundColor = "rgb(" + r + "," + g + "," + b + ")"; if (isRgbw) whites[e] = parseInt(w); selectSlot(csel); } gId('sliderW').value = whites[csel]; if (i.cct && i.cct>=0) gId("sliderA").value = i.cct; gId('sliderSpeed').value = i.sx; gId('sliderIntensity').value = i.ix; /* gId('sliderC1').value = i.f1x ? i.f1x : 0; gId('sliderC2').value = i.f2x ? i.f2x : 0; gId('sliderC3').value = i.f3x ? i.f3x : 0; */ if (s.error && s.error != 0) { var errstr = ""; switch (s.error) { case 10: errstr = "Could not mount filesystem!"; break; case 11: errstr = "Not enough space to save preset!"; break; case 12: errstr = "Preset not found."; break; case 13: errstr = "Missing IR.json."; break; case 19: errstr = "A filesystem error has occured."; break; } showToast('Error ' + s.error + ": " + errstr, true); } selectedPal = i.pal; selectedFx = i.fx; updateUI(); } var jsonTimeout; var reqsLegal = false; function requestJson(command=null) { gId('connind').style.backgroundColor = "var(--c-r)"; if (command && !reqsLegal) return; //stop post requests from chrome onchange event on page restore if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000); var req = null; var url = (loc?`http://${locip}`:'') + '/json/si'; var useWs = (ws && ws.readyState === WebSocket.OPEN); var type = command ? 'post':'get'; if (command) { if (useWs || !command.ps) command.v = true; // force complete /json/si API response command.time = Math.floor(Date.now() / 1000); req = JSON.stringify(command); if (req.length > 1000) useWs = false; //do not send very long requests over websocket }; if (useWs) { ws.send(req?req:'{"v":true}'); return; } else if (command && command.ps) { //refresh UI if we don't use WS (async loading of presets) setTimeout(requestJson,200); } fetch(url, { method: type, headers: { "Content-type": "application/json; charset=UTF-8" }, body: req }) .then(res => { if (!res.ok) showErrorToast(); return res.json(); }) .then(json => { clearTimeout(jsonTimeout); jsonTimeout = null; lastUpdate = new Date(); clearErrorToast(); gId('connind').style.backgroundColor = "var(--c-g)"; if (!json) { showToast('Empty response', true); return; } if (json.success) return; if (json.info) { lastinfo = json.info; parseInfo(); if (isInfo) populateInfo(lastinfo); } var s = json.state ? json.state : json; readState(s); reqsLegal = true; }) .catch(function (error) { showToast(error, true); console.log(error); }); } function togglePower() { isOn = !isOn; var obj = {"on": isOn}; requestJson(obj); } function toggleInfo() { if (isNodes) toggleNodes(); isInfo = !isInfo; if (isInfo) requestJson(); gId('info').style.transform = (isInfo) ? "translateY(0px)":"translateY(100%)"; gId('buttonI').className = (isInfo) ? "active":""; } function toggleNodes() { if (isInfo) toggleInfo(); isNodes = !isNodes; if (isNodes) loadNodes(); gId('nodes').style.transform = (isNodes) ? "translateY(0px)":"translateY(100%)"; gId('buttonNodes').className = (isNodes) ? "active":""; } /* function tglBri(b=null) { if (b===null) b = gId(`briwrap`).style.display === "block"; gId('briwrap').style.display = !b ? "block":"none"; gId('buttonBri').className = !b ? "active":""; size(); } */ function tglCP() { var p = gId('buttonCP').className === "active"; gId('buttonCP').className = !p ? "active":""; gId('picker').style.display = !p ? "block":"none"; gId('vwrap').style.display = !p ? "block":"none"; gId('rgbwrap').style.display = !p ? "block":"none"; var csl = gId('Slots').style.display === "block"; gId('Slots').style.display = !csl ? "block":"none"; //var ps = gId(`Presets`).style.display === "block"; //gId('Presets').style.display = !ps ? "block":"none"; } function tglCs(i) { var pss = gId(`p${i}cstgl`).checked; gId(`p${i}o1`).style.display = pss? "block" : "none"; gId(`p${i}o2`).style.display = !pss? "block" : "none"; } function selSeg(s) { var sel = gId(`seg${s}sel`).checked; var obj = {"seg": {"id": s, "sel": sel}}; requestJson(obj); } function tglPalDropdown() { var p = gId('palDropdown').style; p.display = (p.display==='block'?'none':'block'); gId('fxDropdown').style.display = 'none'; if (p.display==='block') gId('palDropdown').scrollIntoView({ behavior: 'smooth', block: 'center', }); } function tglFxDropdown() { var p = gId('fxDropdown').style; p.display = (p.display==='block'?'none':'block'); gId('palDropdown').style.display = 'none'; if (p.display==='block') gId('fxDropdown').scrollIntoView({ behavior: 'smooth', block: 'center', }); } function setSegPwr(s) { var obj = {"seg": {"id": s, "on": !powered[s]}}; requestJson(obj); } function setSegBri(s) { var obj = {"seg": {"id": s, "bri": parseInt(gId(`seg${s}bri`).value)}}; requestJson(obj); } function setEffect(ind = 0) { tglFxDropdown(); var obj = {"seg": {"fx": parseInt(ind), "fxdef":true}}; // fxdef sets effect parameters to default values, TODO add client setting requestJson(obj); } function setPalette(paletteId = null) { tglPalDropdown(); var obj = {"seg": {"pal": paletteId}}; requestJson(obj); } function setBri() { var obj = {"bri": parseInt(gId('sliderBri').value)}; requestJson(obj); } function setSpeed() { var obj = {"seg": {"sx": parseInt(gId('sliderSpeed').value)}}; requestJson(obj); } function setIntensity() { var obj = {"seg": {"ix": parseInt(gId('sliderIntensity').value)}}; requestJson(obj); } function setLor(i) { var obj = {"lor": i}; requestJson(obj); } function setPreset(i) { var obj = {"ps": i}; if (isPlaylist(i)) obj.on = true; showToast("Loading preset " + pName(i) +" (" + i + ")"); requestJson(obj); } function selectSlot(b) { csel = b; var cd = gId('csl').children; for (let i = 0; i < cd.length; i++) cd[i].classList.remove('xxs-w'); cd[b].classList.add('xxs-w'); setPicker(cd[b].style.backgroundColor); gId('sliderW').value = whites[b]; redrawPalPrev(); updatePSliders(); } var lasth = 0; function pC(col) { if (col == "rnd") { col = {h: 0, s: 0, v: 100}; col.s = Math.floor((Math.random() * 50) + 50); do { col.h = Math.floor(Math.random() * 360); } while (Math.abs(col.h - lasth) < 50); lasth = col.h; } setPicker(col); setColor(0); } function updatePSliders() { //update RGB sliders var col = cpick.color.rgb; gId('sliderR').value = col.r; gId('sliderG').value = col.g; gId('sliderB').value = col.b; //update hex field var str = cpick.color.hexString.substring(1); var w = whites[csel]; if (w > 0) str += w.toString(16); //update value slider var v = gId('sliderV'); v.value = cpick.color.value; //background color as if color had full value var hsv = {"h":cpick.color.hue,"s":cpick.color.saturation,"v":100}; var c = iro.Color.hsvToRgb(hsv); var cs = 'rgb('+c.r+','+c.g+','+c.b+')'; v.nextElementSibling.style.backgroundImage = `linear-gradient(90deg, #000 0%, ${cs})`; //update Kelvin slider gId('sliderK').value = cpick.color.kelvin; } function setPicker(rgb) { var c = new iro.Color(rgb); if (c.value > 0) cpick.color.set(c); else cpick.color.setChannel('hsv', 'v', 0); } function fromV() { cpick.color.setChannel('hsv', 'v', d.getElementById('sliderV').value); } function fromK() { cpick.color.set({ kelvin: d.getElementById('sliderK').value }); } function fromRgb() { var r = gId('sliderR').value; var g = gId('sliderG').value; var b = gId('sliderB').value; setPicker(`rgb(${r},${g},${b})`); setColor(0); } // sets color from picker: 0=all, 1=leaving picker/HSV, 2=ignore white channel function setColor(sr) { var cd = gId('csl').children; // color slots if (sr == 1 && cd[csel].style.backgroundColor == 'rgb(0, 0, 0)') cpick.color.setChannel('hsv', 'v', 100); cd[csel].style.backgroundColor = cpick.color.rgbString; if (sr != 2) whites[csel] = parseInt(gId('sliderW').value); var col = cpick.color.rgb; var obj = {"seg": {"col": [[col.r, col.g, col.b, whites[csel]],[],[]]}}; if (sr==1 || gId(`picker`).style.display !== "block") obj.seg.fx = 0; if (csel == 1) { obj = {"seg": {"col": [[],[col.r, col.g, col.b, whites[csel]],[]]}}; } else if (csel == 2) { obj = {"seg": {"col": [[],[],[col.r, col.g, col.b, whites[csel]]]}}; } requestJson(obj); } function setBalance(b) { var obj = {"seg": {"cct": parseInt(b)}}; requestJson(obj); } var hc = 0; setInterval(()=>{if (!isInfo) return; hc+=18; if (hc>300) hc=0; if (hc>200)hc=306; if (hc==144) hc+=36; if (hc==108) hc+=18; gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;}, 910); function openGH() { window.open("https://github.com/Aircoookie/WLED/wiki"); } var cnfr = false; function cnfReset() { if (!cnfr) { var bt = gId('resetbtn'); bt.style.color = "#f00"; bt.innerHTML = "Confirm Reboot"; cnfr = true; return; } window.location.href = "/reset"; } function loadPalettesData(callback = null) { if (palettesData) return; const lsKey = "wledPalx"; var palettesDataJson = localStorage.getItem(lsKey); if (palettesDataJson) { try { palettesDataJson = JSON.parse(palettesDataJson); if (palettesDataJson && palettesDataJson.vid == lastinfo.vid) { palettesData = palettesDataJson.p; if (callback) callback(); //redrawPalPrev() return; } } catch (e) {} } palettesData = {}; getPalettesData(0, ()=>{ localStorage.setItem(lsKey, JSON.stringify({ p: palettesData, vid: lastinfo.vid })); if (callback) setTimeout(callback, 99); //redrawPalPrev() }); } function getPalettesData(page, callback) { var url = (loc?`http://${locip}`:'') + `/json/palx?page=${page}`; fetch(url, { method: 'get', headers: { "Content-type": "application/json; charset=UTF-8" } }) .then((res)=>{ if (!res.ok) showErrorToast(); return res.json(); }) .then((json)=>{ palettesData = Object.assign({}, palettesData, json.p); if (page < json.m) setTimeout(()=>{ getPalettesData(page + 1, callback); }, 50); else callback(); }) .catch((e)=>{ showToast(e, true); }); } function search(f,l=null) { f.nextElementSibling.style.display=(f.value!=='')?'block':'none'; if (!l) return; var el = gId(l).querySelectorAll('.lstI'); for (i = 0; i < el.length; i++) { var it = el[i]; var itT = it.querySelector('.lstIname').innerText.toUpperCase(); it.style.display = itT.indexOf(f.value.toUpperCase())>-1?'':'none'; } } function clean(c) { c.style.display='none'; var i=c.previousElementSibling; i.value=''; i.focus(); i.dispatchEvent(new Event('input')); } function unfocusSliders() { gId("sliderBri").blur(); gId("sliderSpeed").blur(); gId("sliderIntensity").blur(); } //sliding UI const _C = d.querySelector('.container'), N = 1; let iSlide = 0, x0 = null, scrollS = 0, locked = false, w; function unify(e) { return e.changedTouches ? e.changedTouches[0] : e; } function hasIroClass(classList) { for (var i = 0; i < classList.length; i++) { var element = classList[i]; if (element.startsWith('Iro')) return true; } return false; } function lock(e) { var l = e.target.classList; var pl = e.target.parentElement.classList; if (l.contains('noslide') || hasIroClass(l) || hasIroClass(pl)) return; x0 = unify(e).clientX; scrollS = gEBCN("tabcontent")[iSlide].scrollTop; _C.classList.toggle('smooth', !(locked = true)); } function move(e) { if(!locked) return; var clientX = unify(e).clientX; var dx = clientX - x0; var s = Math.sign(dx); var f = +(s*dx/w).toFixed(2); if((clientX != 0) && (iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) && f > 0.12 && gEBCN("tabcontent")[iSlide].scrollTop == scrollS) { _C.style.setProperty('--i', iSlide -= s); f = 1 - f; updateTablinks(iSlide); } _C.style.setProperty('--f', f); _C.classList.toggle('smooth', !(locked = false)); x0 = null; } function size() { var h = gId('top').clientHeight; sCol('--th', h + "px"); sCol("--tp", h - (gId(`briwrap`).style.display === "block" ? 0 : gId(`briwrap`).clientTop) + "px"); sCol("--bh", "0px"); } function mergeDeep(target, ...sources) { if (!sources.length) return target; const source = sources.shift(); if (isObj(target) && isObj(source)) { for (const key in source) { if (isObj(source[key])) { if (!target[key]) Object.assign(target, { [key]: {} }); mergeDeep(target[key], source[key]); } else { Object.assign(target, { [key]: source[key] }); } } } return mergeDeep(target, ...sources); } size(); window.addEventListener('resize', size, false); _C.addEventListener('mousedown', lock, false); _C.addEventListener('touchstart', lock, false); _C.addEventListener('mouseout', move, false); _C.addEventListener('mouseup', move, false); _C.addEventListener('touchend', move, false);