Introduce common.js in settings pages

This commit is contained in:
Blaz Kristan 2024-09-17 16:26:11 +02:00
parent ac8f919304
commit ceed494cf7
15 changed files with 275 additions and 662 deletions

View File

@ -116,7 +116,8 @@ async function minify(str, type = "plain") {
} else if (type == "css-minify") { } else if (type == "css-minify") {
return new CleanCSS({}).minify(str).styles; return new CleanCSS({}).minify(str).styles;
} else if (type == "js-minify") { } else if (type == "js-minify") {
return await minifyHtml('<script>' + str + '</script>', options).replace(/<[\/]*script>/g, ''); let js = await minifyHtml('<script>' + str + '</script>', options);
return js.replace(/<[\/]*script>/g, '');
} else if (type == "html-minify") { } else if (type == "html-minify") {
return await minifyHtml(str, options); return await minifyHtml(str, options);
} }
@ -252,6 +253,12 @@ writeChunks(
str str
.replace("%%", "%") .replace("%%", "%")
}, },
{
file: "common.js",
name: "JS_common",
method: "gzip",
filter: "js-minify",
},
{ {
file: "settings.htm", file: "settings.htm",
name: "PAGE_settings", name: "PAGE_settings",

118
wled00/data/common.js Normal file
View File

@ -0,0 +1,118 @@
var d=document;
var loc = false, locip, locproto = "http:";
function H(pg="") { window.open("https://kno.wled.ge/"+pg); }
function GH() { window.open("https://github.com/Aircoookie/WLED"); }
function gId(c) { return d.getElementById(c); } // getElementById
function cE(e) { return d.createElement(e); } // createElement
function gEBCN(c) { return d.getElementsByClassName(c); } // getElementsByClassName
function gN(s) { return d.getElementsByName(s)[0]; } // getElementsByName
function isE(o) { return Object.keys(o).length === 0; } // isEmpty
function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); } // isObject
function isN(n) { return !isNaN(parseFloat(n)) && isFinite(n); } // isNumber
// https://stackoverflow.com/questions/3885817/how-do-i-check-that-a-number-is-float-or-integer
function isF(n) { return n === +n && n !== (n|0); } // isFloat
function isI(n) { return n === +n && n === (n|0); } // isInteger
function toggle(el) { gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide"); }
function tooltip(cont=null) {
d.querySelectorAll((cont?cont+" ":"")+"[title]").forEach((element)=>{
element.addEventListener("mouseover", ()=>{
// save title
element.setAttribute("data-title", element.getAttribute("title"));
const tooltip = d.createElement("span");
tooltip.className = "tooltip";
tooltip.textContent = element.getAttribute("title");
// prevent default title popup
element.removeAttribute("title");
let { top, left, width } = element.getBoundingClientRect();
d.body.appendChild(tooltip);
const { offsetHeight, offsetWidth } = tooltip;
const offset = element.classList.contains("sliderwrap") ? 4 : 10;
top -= offsetHeight + offset;
left += (width - offsetWidth) / 2;
tooltip.style.top = top + "px";
tooltip.style.left = left + "px";
tooltip.classList.add("visible");
});
element.addEventListener("mouseout", ()=>{
d.querySelectorAll('.tooltip').forEach((tooltip)=>{
tooltip.classList.remove("visible");
d.body.removeChild(tooltip);
});
// restore title
element.setAttribute("title", element.getAttribute("data-title"));
});
});
};
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true, preGetV = undefined, postGetV = undefined) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
if (preGetV) preGetV();
GetV();
if (postGetV) postGetV();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function getLoc() {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 1) paths.pop(); // remove subpage (or "settings")
if (paths.length > 0 && paths[paths.length-1]=="settings") paths.pop(); // remove "settings"
if (paths.length > 1) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
}
function getURL(path) { return (loc ? locproto + "//" + locip : "") + path; }
function B() { window.open(getURL("/settings"),"_self"); }
var timeout;
function showToast(text, error = false) {
var x = gId("toast");
if (!x) return;
x.innerHTML = text;
x.className = error ? "error":"show";
clearTimeout(timeout);
x.style.animation = 'none';
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
}
function uploadFile(fileObj, name) {
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload");
var formData = new FormData();
formData.append("data", fileObj.files[0], name);
req.send(formData);
fileObj.value = '';
return false;
}

View File

@ -608,8 +608,8 @@
} }
function generatePaletteDivs() { function generatePaletteDivs() {
const palettesDiv = d.getElementById("palettes"); const palettesDiv = gId("palettes");
const staticPalettesDiv = d.getElementById("staticPalettes"); const staticPalettesDiv = gId("staticPalettes");
const paletteDivs = Array.from(palettesDiv.children).filter((child) => { const paletteDivs = Array.from(palettesDiv.children).filter((child) => {
return child.id.match(/^palette\d$/); // match only elements with id starting with "palette" followed by a single digit return child.id.match(/^palette\d$/); // match only elements with id starting with "palette" followed by a single digit
}); });
@ -620,25 +620,25 @@
for (let i = 0; i < paletteArray.length; i++) { for (let i = 0; i < paletteArray.length; i++) {
const palette = paletteArray[i]; const palette = paletteArray[i];
const paletteDiv = d.createElement("div"); const paletteDiv = cE("div");
paletteDiv.id = `palette${i}`; paletteDiv.id = `palette${i}`;
paletteDiv.classList.add("palette"); paletteDiv.classList.add("palette");
const thisKey = Object.keys(palette)[0]; const thisKey = Object.keys(palette)[0];
paletteDiv.dataset.colarray = JSON.stringify(palette[thisKey]); paletteDiv.dataset.colarray = JSON.stringify(palette[thisKey]);
const gradientDiv = d.createElement("div"); const gradientDiv = cE("div");
gradientDiv.id = `paletteGradient${i}` gradientDiv.id = `paletteGradient${i}`
const buttonsDiv = d.createElement("div"); const buttonsDiv = cE("div");
buttonsDiv.id = `buttonsDiv${i}`; buttonsDiv.id = `buttonsDiv${i}`;
buttonsDiv.classList.add("buttonsDiv") buttonsDiv.classList.add("buttonsDiv")
const sendSpan = d.createElement("span"); const sendSpan = cE("span");
sendSpan.id = `sendSpan${i}`; sendSpan.id = `sendSpan${i}`;
sendSpan.onclick = function() {initiateUpload(i)}; sendSpan.onclick = function() {initiateUpload(i)};
sendSpan.setAttribute('title', `Send current editor to slot ${i}`); // perhaps Save instead of Send? sendSpan.setAttribute('title', `Send current editor to slot ${i}`); // perhaps Save instead of Send?
sendSpan.innerHTML = svgSave; sendSpan.innerHTML = svgSave;
sendSpan.classList.add("sendSpan") sendSpan.classList.add("sendSpan")
const editSpan = d.createElement("span"); const editSpan = cE("span");
editSpan.id = `editSpan${i}`; editSpan.id = `editSpan${i}`;
editSpan.onclick = function() {loadForEdit(i)}; editSpan.onclick = function() {loadForEdit(i)};
editSpan.setAttribute('title', `Copy slot ${i} palette to editor`); editSpan.setAttribute('title', `Copy slot ${i} palette to editor`);

View File

@ -882,10 +882,8 @@
hostnameLabel(); hostnameLabel();
})(); })();
function gId(id) { function gId(e) {return d.getElementById(e);}
return d.getElementById(id); function cE(e) {return d.createElement(e);}
}
function hostnameLabel() { function hostnameLabel() {
const link = gId("wledEdit"); const link = gId("wledEdit");
link.href = WLED_URL + "/edit"; link.href = WLED_URL + "/edit";
@ -1675,7 +1673,7 @@
} }
function createCanvas(width, height) { function createCanvas(width, height) {
const canvas = d.createElement("canvas"); const canvas = cE("canvas");
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
@ -1719,7 +1717,7 @@
const blob = new Blob([text], { type: mimeType }); const blob = new Blob([text], { type: mimeType });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const anchorElement = d.createElement("a"); const anchorElement = cE("a");
anchorElement.href = url; anchorElement.href = url;
anchorElement.download = `${filename}.${fileExtension}`; anchorElement.download = `${filename}.${fileExtension}`;
@ -1790,7 +1788,7 @@
hideElement = "preview" hideElement = "preview"
) { ) {
const hide = gId(hideElement); const hide = gId(hideElement);
const toast = d.createElement("div"); const toast = cE("div");
const wait = 100; const wait = 100;
toast.style.animation = "fadeIn"; toast.style.animation = "fadeIn";
@ -1799,14 +1797,14 @@
toast.classList.add("toast", type); toast.classList.add("toast", type);
const body = d.createElement("span"); const body = cE("span");
body.classList.add("toast-body"); body.classList.add("toast-body");
body.textContent = message; body.textContent = message;
toast.appendChild(body); toast.appendChild(body);
const progress = d.createElement("div"); const progress = cE("div");
progress.classList.add("toast-progress"); progress.classList.add("toast-progress");
progress.style.animation = "progress"; progress.style.animation = "progress";
@ -1831,7 +1829,7 @@
function carousel(id, images, delay = 3000) { function carousel(id, images, delay = 3000) {
let index = 0; let index = 0;
const carousel = d.createElement("div"); const carousel = cE("div");
carousel.classList.add("carousel"); carousel.classList.add("carousel");
images.forEach((canvas, i) => { images.forEach((canvas, i) => {
@ -1959,7 +1957,7 @@
let errorElement = parent.querySelector(".error-message"); let errorElement = parent.querySelector(".error-message");
if (!errorElement) { if (!errorElement) {
errorElement = d.createElement("div"); errorElement = cE("div");
errorElement.classList.add("error-message"); errorElement.classList.add("error-message");
parent.appendChild(errorElement); parent.appendChild(errorElement);
} }

View File

@ -4,53 +4,12 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>WLED Settings</title> <title>WLED Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script> <script>
var d=document;
var loc = false, locip, locproto = "http:";
function gId(n){return d.getElementById(n);}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S() { function S() {
let l = window.location; getLoc();
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 1) {
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
loadJS(getURL('/settings/s.js?p=0'), false); // If we set async false, file is loaded and executed, then next statement is processed loadJS(getURL('/settings/s.js?p=0'), false); // If we set async false, file is loaded and executed, then next statement is processed
} }
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script> </script>
<style> <style>
body { body {

View File

@ -4,62 +4,19 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>2D Set-up</title> <title>2D Set-up</title>
<script src="common.js" async type="text/javascript"></script>
<script> <script>
var d=document;
var loc = false, locip, locproto = "http:";
var maxPanels=64; var maxPanels=64;
var ctx = null; // WLEDMM var ctx = null;
function H(){window.open("https://kno.wled.ge/features/2D");}
function B(){window.open(getURL("/settings"),"_self");}
function gId(n){return d.getElementById(n);}
function fS(){d.Sf.submit();} // <button type=submit> sometimes didn't work function fS(){d.Sf.submit();} // <button type=submit> sometimes didn't work
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript function S() {
function loadJS(FILE_URL, async = true) { getLoc();
let scE = d.createElement("script"); loadJS(getURL('/settings/s.js?p=10'), false, undefined, ()=>{
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
UI(); UI();
Sf.MPC.setAttribute("max",maxPanels); Sf.MPC.setAttribute("max",maxPanels);
}); }); // If we set async false, file is loaded and executed, then next statement is processed
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S() {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
paths.pop(); // remove "2d"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
loadJS(getURL('/settings/s.js?p=10'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/2D'); if (loc) d.Sf.action = getURL('/settings/2D');
} }
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
function UI() { function UI() {
if (gId("somp").value === "0") { if (gId("somp").value === "0") {
@ -71,29 +28,6 @@
draw(); draw();
} }
var timeout;
function showToast(text, error = false)
{
var x = gId("toast");
x.innerHTML = text;
x.className = error ? "error":"show";
clearTimeout(timeout);
x.style.animation = 'none';
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
}
function uploadFile(name) {
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload");
var formData = new FormData();
formData.append("data", d.Sf.data.files[0], name);
req.send(formData);
d.Sf.data.value = '';
return false;
}
function addPanels() { function addPanels() {
let c = parseInt(d.Sf.MPC.value); let c = parseInt(d.Sf.MPC.value);
let i = gId("panels").children.length; let i = gId("panels").children.length;
@ -308,7 +242,7 @@ Y:<input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()">
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H('features/2D')">?</button></div>
<button type="button" onclick="B()">Back</button><button type="button" onclick="fS()">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="button" onclick="fS()">Save</button><hr>
</div> </div>
<h2>2D setup</h2> <h2>2D setup</h2>
@ -351,7 +285,7 @@ Y:<input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()">
<hr class="sml"> <hr class="sml">
<div id="MD"></div> <div id="MD"></div>
<canvas id="canvas"></canvas> <canvas id="canvas"></canvas>
<div id="json" >Gap file: <input type="file" name="data" accept=".json"><button type="button" class="sml" onclick="uploadFile('/2d-gaps.json')">Upload</button></div> <div id="json" >Gap file: <input type="file" name="data" accept=".json"><button type="button" class="sml" onclick="uploadFile(d.Sf.data,'/2d-gaps.json')">Upload</button></div>
<i>Note: Gap file is a <b>.json</b> file containing an array with number of elements equal to the matrix size.<br> <i>Note: Gap file is a <b>.json</b> file containing an array with number of elements equal to the matrix size.<br>
A value of -1 means that pixel at that position is missing, a value of 0 means never paint that pixel, and 1 means regular pixel.</i> A value of -1 means that pixel at that position is missing, a value of 0 means never paint that pixel, and 1 means regular pixel.</i>
</div> </div>

View File

@ -4,88 +4,46 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="utf-8"> <meta charset="utf-8">
<title>DMX Settings</title> <title>DMX Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script> <script>
var d=document; function HW(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX");}
var loc = false, locip, locproto = "http:";
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX");}
function B(){window.open(getURL("/settings"),"_self");}
function GCH(num) { function GCH(num) {
d.getElementById('dmxchannels').innerHTML += ""; gId('dmxchannels').innerHTML += "";
for (i=0;i<num;i++) { for (i=0;i<num;i++) {
d.getElementById('dmxchannels').innerHTML += "<span id=CH" + (i+1) + "s >Channel " + (i+1) + ": <select name=CH" + (i+1) + " id=\"CH" + (i+1) + "\"><option value=0>Set to 0</option><option value=1>Red</option><option value=2>Green</option><option value=3>Blue</option><option value=4>White</option><option value=5>Shutter (Brightness)</option><option value=6>Set to 255</option></select></span><br />\n"; gId('dmxchannels').innerHTML += "<span id=CH" + (i+1) + "s >Channel " + (i+1) + ": <select name=CH" + (i+1) + " id=\"CH" + (i+1) + "\"><option value=0>Set to 0</option><option value=1>Red</option><option value=2>Green</option><option value=3>Blue</option><option value=4>White</option><option value=5>Shutter (Brightness)</option><option value=6>Set to 255</option></select></span><br />\n";
} }
} }
function mMap(){ function mMap(){
numCh=document.Sf.CN.value; numCh=document.Sf.CN.value;
numGap=document.Sf.CG.value; numGap=document.Sf.CG.value;
if (parseInt(numCh)>parseInt(numGap)) { if (parseInt(numCh)>parseInt(numGap)) {
d.getElementById("gapwarning").style.display="block"; gId("gapwarning").style.display="block";
} else { } else {
d.getElementById("gapwarning").style.display="none"; gId("gapwarning").style.display="none";
} }
for (i=0;i<15;i++) { for (i=0;i<15;i++) {
if (i>=numCh) { if (i>=numCh) {
d.getElementById("CH"+(i+1) + "s").style.opacity = "0.5"; gId("CH"+(i+1) + "s").style.opacity = "0.5";
d.getElementById("CH"+(i+1)).disabled = true; gId("CH"+(i+1)).disabled = true;
} else { } else {
d.getElementById("CH"+(i+1) + "s").style.opacity = "1"; gId("CH"+(i+1) + "s").style.opacity = "1";
d.getElementById("CH"+(i+1)).disabled = false; gId("CH"+(i+1)).disabled = false;
} }
} }
} }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GCH(15);GetV();mMap();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S(){ function S(){
let l = window.location; getLoc();
if (l.protocol == "file:") { loadJS(getURL('/settings/s.js?p=7'), false, ()=>{GCH(15);}, ()=>{mMap();}); // If we set async false, file is loaded and executed, then next statement is processed
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
paths.pop(); // remove "dmx"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
loadJS(getURL('/settings/s.js?p=7'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/dmx'); if (loc) d.Sf.action = getURL('/settings/dmx');
} }
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="HW()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div> </div>
<h2>Imma firin ma lazer (if it has DMX support)</h2><!-- TODO: Change to something less-meme-related //--> <h2>Imma firin ma lazer (if it has DMX support)</h2><!-- TODO: Change to something less-meme-related //-->

View File

@ -4,20 +4,12 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>LED Settings</title> <title>LED Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script> <script>
var d=document,laprev=55,maxB=1,maxD=1,maxA=1,maxV=0,maxM=4000,maxPB=2048,maxL=1664,maxCO=5,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32 var laprev=55,maxB=1,maxD=1,maxA=1,maxV=0,maxM=4000,maxPB=2048,maxL=1664,maxCO=5,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
var oMaxB=1; var oMaxB=1;
d.ledTypes = [/*{i:22,c:1,t:"D",n:"WS2812"},{i:42,c:6,t:"AA",n:"PWM CCT"}*/]; // filled from GetV()
d.um_p = [];
d.rsvd = [];
d.ro_gpio = [];
d.max_gpio = 50;
var customStarts=false,startsDirty=[]; var customStarts=false,startsDirty=[];
var loc = false, locip, locproto = "http:"; function off(n) { gN(n).value = -1;}
function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");}
function B(){window.open(getURL("/settings"),"_self");}
function gId(n){return d.getElementById(n);}
function off(n){d.getElementsByName(n)[0].value = -1;}
// these functions correspond to C macros found in const.h // these functions correspond to C macros found in const.h
function gT(t) { for (let type of d.ledTypes) if (t == type.i) return type; } // getType from available ledTypes function gT(t) { for (let type of d.ledTypes) if (t == type.i) return type; } // getType from available ledTypes
function isPWM(t) { return gT(t).t.charAt(0) === "A"; } // is PWM type function isPWM(t) { return gT(t).t.charAt(0) === "A"; } // is PWM type
@ -31,37 +23,22 @@
function hasCCT(t) { return !!(gT(t).c & 0x04); } // is white CCT enabled function hasCCT(t) { return !!(gT(t).c & 0x04); } // is white CCT enabled
function is16b(t) { return !!(gT(t).c & 0x10); } // is digital 16 bit type function is16b(t) { return !!(gT(t).c & 0x10); } // is digital 16 bit type
function numPins(t){ return Math.max(gT(t).t.length, 1); } // type length determines number of GPIO pins function numPins(t){ return Math.max(gT(t).t.length, 1); } // type length determines number of GPIO pins
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript function S() {
function loadJS(FILE_URL, async = true) { getLoc();
let scE = d.createElement("script"); loadJS(getURL('/settings/s.js?p=2'), false, ()=>{
scE.setAttribute("src", FILE_URL); d.ledTypes = [/*{i:22,c:1,t:"D",n:"WS2812"},{i:42,c:6,t:"AA",n:"PWM CCT"}*/]; // filled from GetV()
scE.setAttribute("type", "text/javascript"); d.um_p = [];
scE.setAttribute("async", async); d.rsvd = [];
d.body.appendChild(scE); d.ro_gpio = [];
// success event d.max_gpio = 50;
scE.addEventListener("load", () => { }, ()=>{
GetV();
checkSi(); checkSi();
setABL(); setABL();
d.Sf.addEventListener("submit", trySubmit); d.Sf.addEventListener("submit", trySubmit);
if (d.um_p[0]==-1) d.um_p.shift(); if (d.um_p[0]==-1) d.um_p.shift();
pinDropdowns(); pinDropdowns();
}); }); // If we set async false, file is loaded and executed, then next statement is processed
// error event if (loc) d.Sf.action = getURL('/settings/leds');
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
var timeout;
function showToast(text, error = false)
{
var x = gId("toast");
x.innerHTML = text;
x.className = error ? "error":"show";
clearTimeout(timeout);
x.style.animation = 'none';
timeout = setTimeout(()=>{ x.className = x.className.replace("show", ""); }, 2900);
} }
function bLimits(b,v,p,m,l,o=5,d=2,a=6) { function bLimits(b,v,p,m,l,o=5,d=2,a=6) {
// maxB - max buses (can be changed if using ESP32 parallel I2S) // maxB - max buses (can be changed if using ESP32 parallel I2S)
@ -403,7 +380,7 @@
} }
function addLEDs(n,init=true) function addLEDs(n,init=true)
{ {
var o = d.getElementsByClassName("iST"); var o = gEBCN("iST");
var i = o.length; var i = o.length;
let disable = (sel,opt) => { sel.querySelectorAll(opt).forEach((o)=>{o.disabled=true;}); } let disable = (sel,opt) => { sel.querySelectorAll(opt).forEach((o)=>{o.disabled=true;}); }
@ -465,10 +442,10 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
</div>`; </div>`;
f.insertAdjacentHTML("beforeend", cn); f.insertAdjacentHTML("beforeend", cn);
// fill led types (credit @netmindz) // fill led types (credit @netmindz)
d.Sf.querySelectorAll("#mLC select[name^=LT]").forEach((sel,n)=>{ f.querySelectorAll("select[name^=LT]").forEach((sel,n)=>{
if (sel.length == 0) { // ignore already updated if (sel.length == 0) { // ignore already updated
for (let type of d.ledTypes) { for (let type of d.ledTypes) {
let opt = d.createElement("option"); let opt = cE("option");
opt.value = type.i; opt.value = type.i;
opt.text = type.n; opt.text = type.n;
if (type.t != undefined && type.t != "") { if (type.t != undefined && type.t != "") {
@ -499,7 +476,7 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
} }
function addCOM(start=0,len=1,co=0) { function addCOM(start=0,len=1,co=0) {
var i = d.getElementsByClassName("com_entry").length; var i = gEBCN("com_entry").length;
if (i >= maxCO) return; if (i >= maxCO) return;
var s = String.fromCharCode((i<10?48:55)+i); var s = String.fromCharCode((i<10?48:55)+i);
var b = `<div class="com_entry"> var b = `<div class="com_entry">
@ -530,7 +507,7 @@ Swap: <select id="xw${s}" name="XW${s}">
} }
function remCOM() { function remCOM() {
var entries = d.getElementsByClassName("com_entry"); var entries = gEBCN("com_entry");
var i = entries.length; var i = entries.length;
if (i === 0) return; if (i === 0) return;
entries[i-1].remove(); entries[i-1].remove();
@ -542,7 +519,7 @@ Swap: <select id="xw${s}" name="XW${s}">
if (_newMaxCOOverrides) { if (_newMaxCOOverrides) {
maxCO = _newMaxCOOverrides; maxCO = _newMaxCOOverrides;
} }
for (let e of d.getElementsByClassName("com_entry")) { for (let e of gEBCN("com_entry")) {
e.remove(); e.remove();
} }
btnCOM(0); btnCOM(0);
@ -578,25 +555,14 @@ Swap: <select id="xw${s}" name="XW${s}">
} }
function checkSi() { //on load, checks whether there are custom start fields function checkSi() { //on load, checks whether there are custom start fields
var cs = false; var cs = false;
for (var i=1; i < d.getElementsByClassName("iST").length; i++) { for (var i=1; i < gEBCN("iST").length; i++) {
var v = parseInt(gId("ls"+(i-1)).value) + parseInt(d.getElementsByName("LC"+(i-1))[0].value); var v = parseInt(gId("ls"+(i-1)).value) + parseInt(gN("LC"+(i-1)).value);
if (v != parseInt(gId("ls"+i).value)) {cs = true; startsDirty[i] = true;} if (v != parseInt(gId("ls"+i).value)) {cs = true; startsDirty[i] = true;}
} }
if (gId("ls0") && parseInt(gId("ls0").value) != 0) {cs = true; startsDirty[0] = true;} if (gId("ls0") && parseInt(gId("ls0").value) != 0) {cs = true; startsDirty[0] = true;}
gId("si").checked = cs; gId("si").checked = cs;
tglSi(cs); tglSi(cs);
} }
function uploadFile(name) {
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload");
var formData = new FormData();
formData.append("data", d.Sf.data.files[0], name);
req.send(formData);
d.Sf.data.value = '';
return false;
}
// https://stackoverflow.com/questions/7346563/loading-local-json-file // https://stackoverflow.com/questions/7346563/loading-local-json-file
function loadCfg(o) { function loadCfg(o) {
var f, fr; var f, fr;
@ -725,7 +691,7 @@ Swap: <select id="xw${s}" name="XW${s}">
} }
// https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option // https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option
function addDropdown(field) { function addDropdown(field) {
let sel = d.createElement('select'); let sel = cE('select');
sel.classList.add("pin"); sel.classList.add("pin");
let inp = d.getElementsByName(field)[0]; let inp = d.getElementsByName(field)[0];
if (inp && inp.tagName === "INPUT" && (inp.type === "text" || inp.type === "number")) { // may also use nodeName if (inp && inp.tagName === "INPUT" && (inp.type === "text" || inp.type === "number")) { // may also use nodeName
@ -750,7 +716,7 @@ Swap: <select id="xw${s}" name="XW${s}">
} }
function addOption(sel,txt,val) { function addOption(sel,txt,val) {
if (sel===null) return; // select object missing if (sel===null) return; // select object missing
let opt = d.createElement("option"); let opt = cE("option");
opt.value = val; opt.value = val;
opt.text = txt; opt.text = txt;
sel.appendChild(opt); sel.appendChild(opt);
@ -760,40 +726,13 @@ Swap: <select id="xw${s}" name="XW${s}">
} }
return opt; return opt;
} }
function S() {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
paths.pop(); // remove "leds"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
loadJS(getURL('/settings/s.js?p=2'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/leds');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H('features/settings/#led-settings')">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div> </div>
<h2>LED &amp; Hardware setup</h2> <h2>LED &amp; Hardware setup</h2>
@ -861,7 +800,7 @@ Swap: <select id="xw${s}" name="XW${s}">
<option value=8>JSON remote</option> <option value=8>JSON remote</option>
</select><span style="cursor: pointer;" onclick="off('IR')">&nbsp;&#x2715;</span><br> </select><span style="cursor: pointer;" onclick="off('IR')">&nbsp;&#x2715;</span><br>
Apply IR change to main segment only: <input type="checkbox" name="MSO"><br> Apply IR change to main segment only: <input type="checkbox" name="MSO"><br>
<div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"><button type="button" class="sml" onclick="uploadFile('/ir.json')">Upload</button><br></div> <div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"><button type="button" class="sml" onclick="uploadFile(d.Sf.data,'/ir.json')">Upload</button><br></div>
<a href="https://kno.wled.ge/interfaces/infrared/" target="_blank">IR info</a><br> <a href="https://kno.wled.ge/interfaces/infrared/" target="_blank">IR info</a><br>
<hr class="sml"> <hr class="sml">
Relay GPIO: <input type="number" min="-1" max="48" name="RL" onchange="UI()" class="xs"><span style="cursor: pointer;" onclick="off('RL')">&nbsp;&#x2715;</span><br> Relay GPIO: <input type="number" min="-1" max="48" name="RL" onchange="UI()" class="xs"><span style="cursor: pointer;" onclick="off('RL')">&nbsp;&#x2715;</span><br>

View File

@ -4,55 +4,9 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="utf-8"> <meta charset="utf-8">
<title>Misc Settings</title> <title>Misc Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script> <script>
var d = document;
var loc = false, locip, locproto = "http:";
function H() { window.open("https://kno.wled.ge/features/settings/#security-settings"); }
function B() { window.open(getURL("/settings"),"_self"); }
function U() { window.open(getURL("/update"),"_self"); } function U() { window.open(getURL("/update"),"_self"); }
function gId(s) { return d.getElementById(s); }
function isObj(o) { return (o && typeof o === 'object' && !Array.isArray(o)); }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
setBckFilename(gId("bckcfg"));
setBckFilename(gId("bckpresets"));
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
var timeout;
function showToast(text, error = false)
{
var x = gId("toast");
x.innerHTML = text;
x.classList.add(error ? "error":"show");
clearTimeout(timeout);
x.style.animation = 'none';
timeout = setTimeout(function(){ x.classList.remove("show"); }, 2900);
}
function uploadFile(fO,name) {
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", getURL("/upload"));
var formData = new FormData();
formData.append("data", fO.files[0], name);
req.send(formData);
fO.value = '';
return false;
}
function checkNum(o) { function checkNum(o) {
const specialkeys = ["Backspace", "Tab", "Enter", "Shift", "Control", "Alt", "Pause", "CapsLock", "Escape", "Space", "PageUp", "PageDown", "End", "Home", "ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", "Insert", "Delete"]; const specialkeys = ["Backspace", "Tab", "Enter", "Shift", "Control", "Alt", "Pause", "CapsLock", "Escape", "Space", "PageUp", "PageDown", "End", "Home", "ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", "Insert", "Delete"];
// true if key is a number or a special key // true if key is a number or a special key
@ -64,36 +18,17 @@
x.setAttribute("download","wled_" + x.getAttribute("download") + (sd=="WLED"?"":("_" +sd))); x.setAttribute("download","wled_" + x.getAttribute("download") + (sd=="WLED"?"":("_" +sd)));
} }
function S() { function S() {
let l = window.location; getLoc();
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
paths.pop(); // remove "sec"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
if (loc) { if (loc) {
gId("bckcfg").setAttribute('href',getURL(gId("bckcfg").pathname)); gId("bckcfg").setAttribute('href',getURL(gId("bckcfg").pathname));
gId("bckpresets").setAttribute('href',getURL(gId("bckpresets").pathname)); gId("bckpresets").setAttribute('href',getURL(gId("bckpresets").pathname));
} }
loadJS(getURL('/settings/s.js?p=6'), false); // If we set async false, file is loaded and executed, then next statement is processed loadJS(getURL('/settings/s.js?p=6'), false, undefined, ()=>{
setBckFilename(gId("bckcfg"));
setBckFilename(gId("bckpresets"));
}); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/sec'); if (loc) d.Sf.action = getURL('/settings/sec');
} }
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script> </script>
<style> <style>
@import url("style.css"); @import url("style.css");
@ -102,7 +37,7 @@
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H('features/settings/#security-settings')">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div> </div>
<h2>Security & Update setup</h2> <h2>Security & Update setup</h2>

View File

@ -4,32 +4,10 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="utf-8"> <meta charset="utf-8">
<title>Sync Settings</title> <title>Sync Settings</title>
<script>var d=document; <script src="common.js" async type="text/javascript"></script>
var loc = false, locip, locproto = "http:"; <script>
function gId(s){return d.getElementById(s);}
function toggle(el){gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide");}
function H(){window.open("https://kno.wled.ge/interfaces/udp-notifier/");}
function B(){window.open(getURL("/settings"),"_self");}
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;} function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} } else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();SetVal();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function FC() function FC()
{ {
for(j=0;j<8;j++) for(j=0;j<8;j++)
@ -55,26 +33,8 @@
function SP(){var p = d.Sf.DI.value; gId("xp").style.display = (p > 0)?"none":"block"; if (p > 0) d.Sf.EP.value = p;} function SP(){var p = d.Sf.DI.value; gId("xp").style.display = (p > 0)?"none":"block"; if (p > 0) d.Sf.EP.value = p;}
function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; case 4048: d.Sf.DI.value = 4048; break; }; SP();FC();} function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; case 4048: d.Sf.DI.value = 4048; break; }; SP();FC();}
function S(){ function S(){
let l = window.location; getLoc();
if (l.protocol == "file:") { loadJS(getURL('/settings/s.js?p=4'), false, undefined, ()=>{SetVal();}); // If we set async false, file is loaded and executed, then next statement is processed
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let paths = l.pathname.slice(1,l.pathname.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
paths.pop(); // remove "sync"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
loadJS(getURL('/settings/s.js?p=4'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/sync'); if (loc) d.Sf.action = getURL('/settings/sync');
} }
function getURL(path) { function getURL(path) {
@ -86,7 +46,7 @@
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post" onsubmit="GC()"> <form id="form_s" name="Sf" method="post" onsubmit="GC()">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H('interfaces/udp-notifier/')">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div> </div>
<h2>Sync setup</h2> <h2>Sync setup</h2>

View File

@ -4,60 +4,19 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="utf-8"> <meta charset="utf-8">
<title>Time Settings</title> <title>Time Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script> <script>
var d=document;
var loc = false, locip, locproto = "http:";
var el=false; var el=false;
var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
function H() { window.open("https://kno.wled.ge/features/settings/#time-settings"); }
function B() { window.open(getURL("/settings"),"_self"); }
function gId(s) { return d.getElementById(s); }
function gN(s) { return d.getElementsByName(s)[0]; }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
BTa();GetV();updLoc();Cs();FC();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S() { function S() {
let l = window.location; getLoc();
if (l.protocol == "file:") { loadJS(getURL('/settings/s.js?p=5'), false, ()=>{BTa();}, ()=>{
loc = true; updLatLon();
locip = localStorage.getItem('locIp'); Cs();
if (!locip) { FC();
locip = prompt("File Mode. Please enter WLED IP!"); }); // If we set async false, file is loaded and executed, then next statement is processed
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
paths.pop(); // remove "time"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
loadJS(getURL('/settings/s.js?p=5'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/time'); if (loc) d.Sf.action = getURL('/settings/time');
} }
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
function expand(o,i) function expand(o,i)
{ {
var t = gId("WD"+i); var t = gId("WD"+i);
@ -141,21 +100,21 @@
td = tr.insertCell(3); td = tr.insertCell(3);
td.innerHTML = `<input name="MD${b}" type="number" class="s" min="0" max="250" value="${d}" required>`; td.innerHTML = `<input name="MD${b}" type="number" class="s" min="0" max="250" value="${d}" required>`;
} }
function getLoc() { function getLatLon() {
if (!el) { if (!el) {
window.addEventListener("message", (event) => { window.addEventListener("message", (event) => {
if (event.origin !== "https://locate.wled.me") return; if (event.origin !== "https://locate.wled.me") return;
if (event.data instanceof Object) { if (event.data instanceof Object) {
d.Sf.LT.value = event.data.lat; d.Sf.LT.value = event.data.lat;
d.Sf.LN.value = event.data.lon; d.Sf.LN.value = event.data.lon;
updLoc(); updLatLon();
} }
}, false); }, false);
el = true; el = true;
} }
window.open("https://locate.wled.me","_blank"); window.open("https://locate.wled.me","_blank");
} }
function updLoc(i) { function updLatLon(i) {
if (parseFloat(d.Sf.LT.value)<0) { d.Sf.LTR.value = "S"; d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); } else d.Sf.LTR.value = "N"; if (parseFloat(d.Sf.LT.value)<0) { d.Sf.LTR.value = "S"; d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); } else d.Sf.LTR.value = "N";
if (parseFloat(d.Sf.LN.value)<0) { d.Sf.LNR.value = "W"; d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); } else d.Sf.LNR.value = "E"; if (parseFloat(d.Sf.LN.value)<0) { d.Sf.LNR.value = "W"; d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); } else d.Sf.LNR.value = "E";
} }
@ -165,7 +124,7 @@
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post" onsubmit="Wd()"> <form id="form_s" name="Sf" method="post" onsubmit="Wd()">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H('features/settings/#time-settings')">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div> </div>
<h2>Time setup</h2> <h2>Time setup</h2>
@ -202,7 +161,7 @@
Current local time is <span class="times">unknown</span>.<br> Current local time is <span class="times">unknown</span>.<br>
Latitude: <select name="LTR"><option value="N">N</option><option value="S">S</option></select><input name="LT" type="number" class="xl" min="0" max="66.6" step="0.01"><br> Latitude: <select name="LTR"><option value="N">N</option><option value="S">S</option></select><input name="LT" type="number" class="xl" min="0" max="66.6" step="0.01"><br>
Longitude: <select name="LNR"><option value="E">E</option><option value="W">W</option></select><input name="LN" type="number" class="xl" min="0" max="180" step="0.01"><br> Longitude: <select name="LNR"><option value="E">E</option><option value="W">W</option></select><input name="LN" type="number" class="xl" min="0" max="180" step="0.01"><br>
<button type="button" id="locbtn" onclick="getLoc()">Get location</button> <button type="button" id="locbtn" onclick="getLatLon()">Get location</button>
<div><i>(opens new tab, only works in browser)</i></div> <div><i>(opens new tab, only works in browser)</i></div>
<div id="sun" class="times"></div> <div id="sun" class="times"></div>
<h3>Clock</h3> <h3>Clock</h3>

View File

@ -4,9 +4,8 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>UI Settings</title> <title>UI Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script> <script>
var d = document;
var loc = false, locip, locproto = "http:";
var initial_ds, initial_st, initial_su, oldUrl; var initial_ds, initial_st, initial_su, oldUrl;
var sett = null; var sett = null;
var l = { var l = {
@ -47,11 +46,6 @@
} }
} }
}; };
function gId(s) { return d.getElementById(s); }
function toggle(el) { gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide"); }
function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
function set(path, obj, val) { function set(path, obj, val) {
var tar = obj; var tar = obj;
var pList = path.split('_'); var pList = path.split('_');
@ -63,23 +57,13 @@
} }
tar[pList[len-1]] = val; tar[pList[len-1]] = val;
} }
var timeout;
function showToast(text, error = false)
{
var x = gId("toast");
x.innerHTML = text;
x.classList.add(error ? "error":"show");
clearTimeout(timeout);
x.style.animation = 'none';
timeout = setTimeout(function(){ x.classList.remove("show"); }, 2900);
}
function addRec(s, path = "", label = null) function addRec(s, path = "", label = null)
{ {
var str = ""; var str = "";
for (let i in s) for (let i in s)
{ {
var fk = path + (path?'_':'') + i; var fk = path + (path?'_':'') + i;
if (isObject(s[i])) { if (isO(s[i])) {
if (label && label[i] && label[i]["LABEL"]) str += `<h3>${label[i]["LABEL"]}</h3>`; if (label && label[i] && label[i]["LABEL"]) str += `<h3>${label[i]["LABEL"]}</h3>`;
str += addRec(s[i], fk, label? label[i] : null); str += addRec(s[i], fk, label? label[i] : null);
} else { } else {
@ -174,57 +158,16 @@
if (d.Sf.DS.value != initial_ds || /*d.Sf.ST.checked != initial_st ||*/ d.Sf.SU.checked != initial_su) d.Sf.submit(); if (d.Sf.DS.value != initial_ds || /*d.Sf.ST.checked != initial_st ||*/ d.Sf.SU.checked != initial_su) d.Sf.submit();
} }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript function S() {
function loadJS(FILE_URL, async = true) { getLoc();
let scE = d.createElement("script"); loadJS(getURL('/settings/s.js?p=3'), false, undefined, ()=>{
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
initial_ds = d.Sf.DS.value; initial_ds = d.Sf.DS.value;
//initial_st = d.Sf.ST.checked; //initial_st = d.Sf.ST.checked;
initial_su = d.Sf.SU.checked; initial_su = d.Sf.SU.checked;
GetLS(); GetLS();
}); }); // If we set async false, file is loaded and executed, then next statement is processed
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S() {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
paths.pop(); // remove "ui"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
loadJS(getURL('/settings/s.js?p=3'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/ui'); if (loc) d.Sf.action = getURL('/settings/ui');
} }
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
function H() { window.open("https://kno.wled.ge/features/settings/#user-interface-settings"); }
function B() { window.open(getURL("/settings"),"_self"); }
function UI() function UI()
{ {
gId('idonthateyou').style.display = (gId('dm').checked) ? 'inline':'none'; gId('idonthateyou').style.display = (gId('dm').checked) ? 'inline':'none';
@ -264,25 +207,13 @@
if (gId("theme_bg_rnd").checked) toggle("Image"); if (gId("theme_bg_rnd").checked) toggle("Image");
gId("theme_bg_rnd").checked = false; gId("theme_bg_rnd").checked = false;
} }
function uploadFile(fO,name) {
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload");
var formData = new FormData();
formData.append("data", fO.files[0], name);
req.send(formData);
fO.value = '';
return false;
}
</script> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H('features/settings/#user-interface-settings')">?</button></div>
<button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button><br> <button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button><br>
<span id="lssuc" style="color:green; display:none">&#10004; Local UI settings saved!</span> <span id="lssuc" style="color:green; display:none">&#10004; Local UI settings saved!</span>
<span id="lserr" style="color:red; display:none">&#9888; Could not access local storage. Make sure it is enabled in your browser.</span><hr> <span id="lserr" style="color:red; display:none">&#9888; Could not access local storage. Make sure it is enabled in your browser.</span><hr>

View File

@ -4,75 +4,55 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>Usermod Settings</title> <title>Usermod Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script> <script>
var d = document;
d.max_gpio = 50;
d.um_p = [];
d.rsvd = [];
d.ro_gpio = [];
d.extra = [];
var umCfg = {}; var umCfg = {};
var pins = [], pinO = [], owner; var pins = [], pinO = [], owner;
var loc = false, locip, locproto = "http:";
var urows; var urows;
var numM = 0; var numM = 0;
function gId(s) { return d.getElementById(s); }
function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); }
function H() { window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings"); }
function B() { window.open(getURL("/settings"),"_self"); }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
GetV();
for (let r of d.rsvd) { pins.push(r); pinO.push("rsvd"); } // reserved pins
if (d.um_p[0]==-1) d.um_p.shift(); // remove filler
d.Sf.SDA.max = d.Sf.SCL.max = d.Sf.MOSI.max = d.Sf.SCLK.max = d.Sf.MISO.max = d.max_gpio-1;
//for (let i of d.getElementsByTagName("input")) if (i.type === "number" && i.name.replace("[]","").substr(-3) === "pin") i.max = d.max_gpio-1;
pinDD(); // convert INPUT to SELECT for pins
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S() { function S() {
let l = window.location; getLoc();
if (l.protocol == "file:") { // load settings and insert values into DOM
loc = true; fetch(getURL('/cfg.json'), {
locip = localStorage.getItem('locIp'); method: 'get'
if (!locip) { })
locip = prompt("File Mode. Please enter WLED IP!"); .then(res => {
localStorage.setItem('locIp', locip); if (!res.ok) gId('lserr').style.display = "inline";
return res.json();
})
.then(json => {
umCfg = json.um;
getPins(json);
urows="";
if (isO(umCfg)) {
for (const [k,o] of Object.entries(umCfg)) {
urows += `<hr><h3>${k}</h3>`;
addField(k,'unknown',o);
}
} }
} else { if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults.";
// detect reverse proxy gId("um").innerHTML = urows;
let path = l.pathname; loadJS(getURL('/settings/s.js?p=8'), false, ()=>{
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/"); d.max_gpio = 50;
if (paths.length > 2) { d.um_p = [];
paths.pop(); // remove "um" d.rsvd = [];
paths.pop(); // remove "settings" d.ro_gpio = [];
locproto = l.protocol; d.extra = [];
loc = true; }, ()=>{
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/'); for (let r of d.rsvd) { pins.push(r); pinO.push("rsvd"); } // reserved pins
} if (d.um_p[0]==-1) d.um_p.shift(); // remove filler
} d.Sf.SDA.max = d.Sf.SCL.max = d.Sf.MOSI.max = d.Sf.SCLK.max = d.Sf.MISO.max = d.max_gpio;
ldS(); //for (let i of d.getElementsByTagName("input")) if (i.type === "number" && i.name.replace("[]","").substr(-3) === "pin") i.max = d.max_gpio;
pinDD(); // convert INPUT to SELECT for pins
}); // If we set async false, file is loaded and executed, then next statement is processed
})
.catch((error)=>{
gId('lserr').style.display = "inline";
console.log(error);
});
if (!numM) gId("um").innerHTML = "No Usermods installed."; if (!numM) gId("um").innerHTML = "No Usermods installed.";
if (loc) d.Sf.action = getURL('/settings/um'); if (loc) d.Sf.action = getURL('/settings/um');
} }
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
// https://stackoverflow.com/questions/3885817/how-do-i-check-that-a-number-is-float-or-integer
function isF(n) { return n === +n && n !== (n|0); }
function isI(n) { return n === +n && n === (n|0); }
function check(o,k) { // input object, pin owner key function check(o,k) { // input object, pin owner key
/* no longer necessary with pin dropdown fields /* no longer necessary with pin dropdown fields
var n = o.name.replace("[]","").substr(-3); var n = o.name.replace("[]","").substr(-3);
@ -220,7 +200,7 @@
} }
// https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option // https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option
function addDD(um,fld) { function addDD(um,fld) {
let sel = d.createElement('select'); let sel = cE('select');
if (typeof(fld) === "string") { // parameter from usermod (field name) if (typeof(fld) === "string") { // parameter from usermod (field name)
if (fld.includes("pin")) sel.classList.add("pin"); if (fld.includes("pin")) sel.classList.add("pin");
um += ":"+fld; um += ":"+fld;
@ -258,7 +238,7 @@
var addDropdown = addDD; // backwards compatibility var addDropdown = addDD; // backwards compatibility
function addO(sel,txt,val) { function addO(sel,txt,val) {
if (sel===null) return; // select object missing if (sel===null) return; // select object missing
let opt = d.createElement("option"); let opt = cE("option");
opt.value = val; opt.value = val;
opt.text = txt; opt.text = txt;
sel.appendChild(opt); sel.appendChild(opt);
@ -284,34 +264,6 @@
function addHB(um) { function addHB(um) {
addI(um + ':help',0,`<button onclick="location.href='https://kno.wled.ge/usermods/${um}'" type="button">?</button>`); addI(um + ':help',0,`<button onclick="location.href='https://kno.wled.ge/usermods/${um}'" type="button">?</button>`);
} }
// load settings and insert values into DOM
function ldS() {
fetch(getURL('/cfg.json'), {
method: 'get'
})
.then(res => {
if (!res.ok) gId('lserr').style.display = "inline";
return res.json();
})
.then(json => {
umCfg = json.um;
getPins(json);
urows="";
if (isO(umCfg)) {
for (const [k,o] of Object.entries(umCfg)) {
urows += `<hr><h3>${k}</h3>`;
addField(k,'unknown',o);
}
}
if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults.";
gId("um").innerHTML = urows;
loadJS(getURL('/settings/s.js?p=8'), false); // If we set async false, file is loaded and executed, then next statement is processed
})
.catch((error)=>{
gId('lserr').style.display = "inline";
console.log(error);
});
}
function svS(e) { function svS(e) {
e.preventDefault(); e.preventDefault();
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914 if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914

View File

@ -4,16 +4,10 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>WiFi Settings</title> <title>WiFi Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script> <script>
var d = document;
var loc = false, locip, locproto = "http:";
var scanLoops = 0, preScanSSID = ""; var scanLoops = 0, preScanSSID = "";
var maxNetworks = 3; var maxNetworks = 3;
function gId(e) { return d.getElementById(e); }
function cE(e) { return d.createElement(e); }
function toggle(el){gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide");}
function H(){window.open("https://kno.wled.ge/features/settings/#wifi-settings");}
function B(){window.open(getURL("/settings"),"_self");}
function N() { function N() {
const button = gId("scan"); const button = gId("scan");
button.disabled = true; button.disabled = true;
@ -137,58 +131,18 @@ Static subnet mask:<br>
entries[i-1].remove(); entries[i-1].remove();
btnWiFi(i-1); btnWiFi(i-1);
} }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = cE("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S() { function S() {
let l = window.location; getLoc();
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
paths.pop(); // remove "wifi"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
loadJS(getURL('/settings/s.js?p=1'), false); // If we set async false, file is loaded and executed, then next statement is processed loadJS(getURL('/settings/s.js?p=1'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/wifi'); if (loc) d.Sf.action = getURL('/settings/wifi');
} }
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H('features/settings/#wifi-settings')">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button><hr>
</div> </div>
<h2>WiFi setup</h2> <h2>WiFi setup</h2>

View File

@ -18,6 +18,7 @@ static const char s_unlock_ota [] PROGMEM = "Please unlock OTA in security setti
static const char s_unlock_cfg [] PROGMEM = "Please unlock settings using PIN code!"; static const char s_unlock_cfg [] PROGMEM = "Please unlock settings using PIN code!";
static const char s_notimplemented[] PROGMEM = "Not implemented"; static const char s_notimplemented[] PROGMEM = "Not implemented";
static const char s_accessdenied[] PROGMEM = "Access Denied"; static const char s_accessdenied[] PROGMEM = "Access Denied";
static const char _common_js[] PROGMEM = "/common.js";
//Is this an IP? //Is this an IP?
static bool isIp(String str) { static bool isIp(String str) {
@ -237,6 +238,10 @@ void initServer()
handleStaticContent(request, "", 200, FPSTR(CONTENT_TYPE_HTML), PAGE_liveview, PAGE_liveview_length); handleStaticContent(request, "", 200, FPSTR(CONTENT_TYPE_HTML), PAGE_liveview, PAGE_liveview_length);
}); });
server.on(_common_js, HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, FPSTR(_common_js), 200, FPSTR(CONTENT_TYPE_JAVASCRIPT), JS_common, JS_common_length);
});
//settings page //settings page
server.on(F("/settings"), HTTP_GET, [](AsyncWebServerRequest *request){ server.on(F("/settings"), HTTP_GET, [](AsyncWebServerRequest *request){
serveSettings(request); serveSettings(request);
@ -511,6 +516,10 @@ void serveJsonError(AsyncWebServerRequest* request, uint16_t code, uint16_t erro
void serveSettingsJS(AsyncWebServerRequest* request) void serveSettingsJS(AsyncWebServerRequest* request)
{ {
if (request->url().indexOf(FPSTR(_common_js)) > 0) {
handleStaticContent(request, FPSTR(_common_js), 200, FPSTR(CONTENT_TYPE_JAVASCRIPT), JS_common, JS_common_length);
return;
}
char buf[SETTINGS_STACK_BUF_SIZE+37]; char buf[SETTINGS_STACK_BUF_SIZE+37];
buf[0] = 0; buf[0] = 0;
byte subPage = request->arg(F("p")).toInt(); byte subPage = request->arg(F("p")).toInt();