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") {
return new CleanCSS({}).minify(str).styles;
} 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") {
return await minifyHtml(str, options);
}
@ -252,6 +253,12 @@ writeChunks(
str
.replace("%%", "%")
},
{
file: "common.js",
name: "JS_common",
method: "gzip",
filter: "js-minify",
},
{
file: "settings.htm",
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() {
const palettesDiv = d.getElementById("palettes");
const staticPalettesDiv = d.getElementById("staticPalettes");
const palettesDiv = gId("palettes");
const staticPalettesDiv = gId("staticPalettes");
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
});
@ -620,25 +620,25 @@
for (let i = 0; i < paletteArray.length; i++) {
const palette = paletteArray[i];
const paletteDiv = d.createElement("div");
const paletteDiv = cE("div");
paletteDiv.id = `palette${i}`;
paletteDiv.classList.add("palette");
const thisKey = Object.keys(palette)[0];
paletteDiv.dataset.colarray = JSON.stringify(palette[thisKey]);
const gradientDiv = d.createElement("div");
const gradientDiv = cE("div");
gradientDiv.id = `paletteGradient${i}`
const buttonsDiv = d.createElement("div");
const buttonsDiv = cE("div");
buttonsDiv.id = `buttonsDiv${i}`;
buttonsDiv.classList.add("buttonsDiv")
const sendSpan = d.createElement("span");
const sendSpan = cE("span");
sendSpan.id = `sendSpan${i}`;
sendSpan.onclick = function() {initiateUpload(i)};
sendSpan.setAttribute('title', `Send current editor to slot ${i}`); // perhaps Save instead of Send?
sendSpan.innerHTML = svgSave;
sendSpan.classList.add("sendSpan")
const editSpan = d.createElement("span");
const editSpan = cE("span");
editSpan.id = `editSpan${i}`;
editSpan.onclick = function() {loadForEdit(i)};
editSpan.setAttribute('title', `Copy slot ${i} palette to editor`);

View File

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

View File

@ -4,53 +4,12 @@
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>WLED Settings</title>
<script src="common.js" async type="text/javascript"></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() {
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 "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
getLoc();
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>
<style>
body {

View File

@ -4,62 +4,19 @@
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>2D Set-up</title>
<script src="common.js" async type="text/javascript"></script>
<script>
var d=document;
var loc = false, locip, locproto = "http:";
var maxPanels=64;
var ctx = null; // WLEDMM
function H(){window.open("https://kno.wled.ge/features/2D");}
function B(){window.open(getURL("/settings"),"_self");}
function gId(n){return d.getElementById(n);}
var ctx = null;
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 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();
function S() {
getLoc();
loadJS(getURL('/settings/s.js?p=10'), false, undefined, ()=>{
UI();
Sf.MPC.setAttribute("max",maxPanels);
});
// 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 we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/2D');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
function UI() {
if (gId("somp").value === "0") {
@ -71,29 +28,6 @@
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() {
let c = parseInt(d.Sf.MPC.value);
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()">
<form id="form_s" name="Sf" method="post">
<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>
</div>
<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">
<div id="MD"></div>
<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>
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>

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 charset="utf-8">
<title>DMX Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script>
var d=document;
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 HW(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX");}
function GCH(num) {
d.getElementById('dmxchannels').innerHTML += "";
gId('dmxchannels').innerHTML += "";
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(){
numCh=document.Sf.CN.value;
numGap=document.Sf.CG.value;
if (parseInt(numCh)>parseInt(numGap)) {
d.getElementById("gapwarning").style.display="block";
gId("gapwarning").style.display="block";
} else {
d.getElementById("gapwarning").style.display="none";
gId("gapwarning").style.display="none";
}
for (i=0;i<15;i++) {
if (i>=numCh) {
d.getElementById("CH"+(i+1) + "s").style.opacity = "0.5";
d.getElementById("CH"+(i+1)).disabled = true;
gId("CH"+(i+1) + "s").style.opacity = "0.5";
gId("CH"+(i+1)).disabled = true;
} else {
d.getElementById("CH"+(i+1) + "s").style.opacity = "1";
d.getElementById("CH"+(i+1)).disabled = false;
gId("CH"+(i+1) + "s").style.opacity = "1";
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(){
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 "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
getLoc();
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
if (loc) d.Sf.action = getURL('/settings/dmx');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script>
<style>@import url("style.css");</style>
</head>
<body onload="S()">
<form id="form_s" name="Sf" method="post">
<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>
</div>
<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 content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>LED Settings</title>
<script src="common.js" async type="text/javascript"></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;
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 loc = false, locip, locproto = "http:";
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;}
function off(n) { gN(n).value = -1;}
// 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 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 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
// 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();
function S() {
getLoc();
loadJS(getURL('/settings/s.js?p=2'), false, ()=>{
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;
}, ()=>{
checkSi();
setABL();
d.Sf.addEventListener("submit", trySubmit);
if (d.um_p[0]==-1) d.um_p.shift();
pinDropdowns();
});
// 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.className = error ? "error":"show";
clearTimeout(timeout);
x.style.animation = 'none';
timeout = setTimeout(()=>{ x.className = x.className.replace("show", ""); }, 2900);
}); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/leds');
}
function bLimits(b,v,p,m,l,o=5,d=2,a=6) {
// maxB - max buses (can be changed if using ESP32 parallel I2S)
@ -403,7 +380,7 @@
}
function addLEDs(n,init=true)
{
var o = d.getElementsByClassName("iST");
var o = gEBCN("iST");
var i = o.length;
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>`;
f.insertAdjacentHTML("beforeend", cn);
// 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
for (let type of d.ledTypes) {
let opt = d.createElement("option");
let opt = cE("option");
opt.value = type.i;
opt.text = type.n;
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) {
var i = d.getElementsByClassName("com_entry").length;
var i = gEBCN("com_entry").length;
if (i >= maxCO) return;
var s = String.fromCharCode((i<10?48:55)+i);
var b = `<div class="com_entry">
@ -530,7 +507,7 @@ Swap: <select id="xw${s}" name="XW${s}">
}
function remCOM() {
var entries = d.getElementsByClassName("com_entry");
var entries = gEBCN("com_entry");
var i = entries.length;
if (i === 0) return;
entries[i-1].remove();
@ -542,7 +519,7 @@ Swap: <select id="xw${s}" name="XW${s}">
if (_newMaxCOOverrides) {
maxCO = _newMaxCOOverrides;
}
for (let e of d.getElementsByClassName("com_entry")) {
for (let e of gEBCN("com_entry")) {
e.remove();
}
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
var cs = false;
for (var i=1; i < d.getElementsByClassName("iST").length; i++) {
var v = parseInt(gId("ls"+(i-1)).value) + parseInt(d.getElementsByName("LC"+(i-1))[0].value);
for (var i=1; i < gEBCN("iST").length; i++) {
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 (gId("ls0") && parseInt(gId("ls0").value) != 0) {cs = true; startsDirty[0] = true;}
gId("si").checked = 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
function loadCfg(o) {
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
function addDropdown(field) {
let sel = d.createElement('select');
let sel = cE('select');
sel.classList.add("pin");
let inp = d.getElementsByName(field)[0];
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) {
if (sel===null) return; // select object missing
let opt = d.createElement("option");
let opt = cE("option");
opt.value = val;
opt.text = txt;
sel.appendChild(opt);
@ -760,40 +726,13 @@ Swap: <select id="xw${s}" name="XW${s}">
}
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>
<style>@import url("style.css");</style>
</head>
<body onload="S()">
<form id="form_s" name="Sf" method="post">
<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>
</div>
<h2>LED &amp; Hardware setup</h2>
@ -861,7 +800,7 @@ Swap: <select id="xw${s}" name="XW${s}">
<option value=8>JSON remote</option>
</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>
<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>
<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>

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 charset="utf-8">
<title>Misc Settings</title>
<script src="common.js" async type="text/javascript"></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 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) {
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
@ -64,36 +18,17 @@
x.setAttribute("download","wled_" + x.getAttribute("download") + (sd=="WLED"?"":("_" +sd)));
}
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 "sec"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
getLoc();
if (loc) {
gId("bckcfg").setAttribute('href',getURL(gId("bckcfg").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');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script>
<style>
@import url("style.css");
@ -102,7 +37,7 @@
<body onload="S()">
<form id="form_s" name="Sf" method="post">
<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>
</div>
<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 charset="utf-8">
<title>Sync Settings</title>
<script>var d=document;
var loc = false, locip, locproto = "http:";
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");}
<script src="common.js" async type="text/javascript"></script>
<script>
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;} }
// 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()
{
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 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(){
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 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
getLoc();
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
if (loc) d.Sf.action = getURL('/settings/sync');
}
function getURL(path) {
@ -86,7 +46,7 @@
<body onload="S()">
<form id="form_s" name="Sf" method="post" onsubmit="GC()">
<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>
</div>
<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 charset="utf-8">
<title>Time Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script>
var d=document;
var loc = false, locip, locproto = "http:";
var el=false;
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() {
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 "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
getLoc();
loadJS(getURL('/settings/s.js?p=5'), false, ()=>{BTa();}, ()=>{
updLatLon();
Cs();
FC();
}); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/time');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
function expand(o,i)
{
var t = gId("WD"+i);
@ -141,21 +100,21 @@
td = tr.insertCell(3);
td.innerHTML = `<input name="MD${b}" type="number" class="s" min="0" max="250" value="${d}" required>`;
}
function getLoc() {
function getLatLon() {
if (!el) {
window.addEventListener("message", (event) => {
if (event.origin !== "https://locate.wled.me") return;
if (event.data instanceof Object) {
d.Sf.LT.value = event.data.lat;
d.Sf.LN.value = event.data.lon;
updLoc();
updLatLon();
}
}, false);
el = true;
}
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.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()">
<form id="form_s" name="Sf" method="post" onsubmit="Wd()">
<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>
</div>
<h2>Time setup</h2>
@ -202,7 +161,7 @@
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>
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 id="sun" class="times"></div>
<h3>Clock</h3>

View File

@ -4,9 +4,8 @@
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>UI Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script>
var d = document;
var loc = false, locip, locproto = "http:";
var initial_ds, initial_st, initial_su, oldUrl;
var sett = null;
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) {
var tar = obj;
var pList = path.split('_');
@ -63,23 +57,13 @@
}
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)
{
var str = "";
for (let i in s)
{
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>`;
str += addRec(s[i], fk, label? label[i] : null);
} 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();
}
// 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();
function S() {
getLoc();
loadJS(getURL('/settings/s.js?p=3'), false, undefined, ()=>{
initial_ds = d.Sf.DS.value;
//initial_st = d.Sf.ST.checked;
initial_su = d.Sf.SU.checked;
GetLS();
});
// 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 we set async false, file is loaded and executed, then next statement is processed
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()
{
gId('idonthateyou').style.display = (gId('dm').checked) ? 'inline':'none';
@ -264,25 +207,13 @@
if (gId("theme_bg_rnd").checked) toggle("Image");
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>
<style>@import url("style.css");</style>
</head>
<body onload="S()">
<form id="form_s" name="Sf" method="post">
<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>
<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>

View File

@ -4,75 +4,55 @@
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>Usermod Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script>
var d = document;
d.max_gpio = 50;
d.um_p = [];
d.rsvd = [];
d.ro_gpio = [];
d.extra = [];
var umCfg = {};
var pins = [], pinO = [], owner;
var loc = false, locip, locproto = "http:";
var urows;
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() {
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);
getLoc();
// load settings and insert values into DOM
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);
}
}
} 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 "um"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
ldS();
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, ()=>{
d.max_gpio = 50;
d.um_p = [];
d.rsvd = [];
d.ro_gpio = [];
d.extra = [];
}, ()=>{
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;
//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 (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
/* no longer necessary with pin dropdown fields
var n = o.name.replace("[]","").substr(-3);
@ -220,7 +200,7 @@
}
// https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option
function addDD(um,fld) {
let sel = d.createElement('select');
let sel = cE('select');
if (typeof(fld) === "string") { // parameter from usermod (field name)
if (fld.includes("pin")) sel.classList.add("pin");
um += ":"+fld;
@ -258,7 +238,7 @@
var addDropdown = addDD; // backwards compatibility
function addO(sel,txt,val) {
if (sel===null) return; // select object missing
let opt = d.createElement("option");
let opt = cE("option");
opt.value = val;
opt.text = txt;
sel.appendChild(opt);
@ -284,34 +264,6 @@
function addHB(um) {
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) {
e.preventDefault();
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914

View File

@ -4,16 +4,10 @@
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>WiFi Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script>
var d = document;
var loc = false, locip, locproto = "http:";
var scanLoops = 0, preScanSSID = "";
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() {
const button = gId("scan");
button.disabled = true;
@ -137,58 +131,18 @@ Static subnet mask:<br>
entries[i-1].remove();
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() {
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 "wifi"
paths.pop(); // remove "settings"
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
}
}
getLoc();
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');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script>
<style>@import url("style.css");</style>
</head>
<body onload="S()">
<form id="form_s" name="Sf" method="post">
<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>
</div>
<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_notimplemented[] PROGMEM = "Not implemented";
static const char s_accessdenied[] PROGMEM = "Access Denied";
static const char _common_js[] PROGMEM = "/common.js";
//Is this an IP?
static bool isIp(String str) {
@ -237,6 +238,10 @@ void initServer()
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
server.on(F("/settings"), HTTP_GET, [](AsyncWebServerRequest *request){
serveSettings(request);
@ -511,6 +516,10 @@ void serveJsonError(AsyncWebServerRequest* request, uint16_t code, uint16_t erro
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];
buf[0] = 0;
byte subPage = request->arg(F("p")).toInt();