Hello all,
I think the software is great, but I don’t always want to have all settings displayed. For me it is clearer if some settings are hidden.
Therefore I implemented a query to check in which user mode I am.
There are 3 user modes: User / Expert and Developer.
The two modes user and expert can be displayed via drop down menu. The expert mode is only activated after entering a password. In the expert mode, further functions are then displayed.
The Developer mode is activated at the bottom via a checkbox and another password request. If this mode is activated, all functions of the current page are displayed.
Now I would like to transfer these 3 user levels to other websites and use these functions there as well. Currently the code is only used on the Settings_UI page.
Do you have an idea how I can realize this?
If you see further optimizations of this code, please let me know :).
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=500">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<title>UI Settings</title>
<script>
var d = document;
var loc = false, locip;
var initial_ds, initial_st, initial_su;
var sett = null;
var l = {
"comp":{
"labels":"Show button labels",
"colors":{
"LABEL":"Color selection methods",
"picker": "Color Wheel",
"rgb": "RGB sliders",
"quick": "Quick color selectors",
"hex": "HEX color input"
},
"pcmbot": "Show bottom tab bar in PC mode",
"pid": "Show preset IDs",
"seglen": "Set segment length instead of stop LED",
"segpwr": "Hide segment power & brightness",
"segexp" : "Always expand first segment",
"css": "Enable custom CSS",
"hdays": "Enable custom Holidays list"
},
"theme":{
"alpha": {
"bg":"Background opacity",
"tab":"Button opacity"
},
"bg":{
"url":"BG image URL",
"random":"Random BG image"
},
"color":{
"bg":"BG HEX color"
}
}
};
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('_');
var len = pList.length;
for(var i = 0; i < len-1; i++) {
var elem = pList[i];
if( !tar[elem] ) tar[elem] = {}
tar = tar[elem];
}
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 (i in s)
{
var fk = path + (path?'_':'') + i;
if (isObject(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 {
var lb = fk;
if (label && label[i]) lb = label[i];
else if (s[i+'LABEL']) lb = s[i+'LABEL'];
if (i.indexOf('LABEL') > 0) continue;
var t = typeof s[i];
if (gId(fk)) { //already exists
if(t === 'boolean')
{
gId(fk).checked = s[i];
} else {
gId(fk).value = s[i];
}
if (gId(fk).previousElementSibling.matches('.l')) {
gId(fk).previousElementSibling.innerHTML = lb;
}
} else {
if(t === 'boolean')
{
str += `${lb}: <input class="agi cb" type="checkbox" id=${fk} ${s[i]?"checked":""}><br>`;
} else if (t === 'number')
{
str += `${lb}: <input class="agi" type="number" id=${fk} value=${s[i]}><br>`;
} else if (t === 'string')
{
str += `${lb}:<br><input class="agi" id=${fk} value=${s[i]}><br>`;
}
}
}
}
return str;
}
function genForm(s) {
var str = "";
str = addRec(s,"",l);
gId('gen').innerHTML = str;
}
function GetLS()
{
sett = localStorage.getItem('wledUiCfg');
if (!sett) gId('lserr').style.display = "inline";
try {
sett = JSON.parse(sett);
} catch (e) {
sett = {};
gId('lserr').style.display = "inline";
gId('lserr').innerHTML = "⚠ Settings JSON parsing failed. (" + e + ")";
}
genForm(sett);
gId('dm').checked = (gId('theme_base').value === 'light');
}
function SetLS()
{
var l = d.querySelectorAll('.agi');
for (var i = 0; i < l.length; i++) {
var e = l[i];
var val = e.classList.contains('cb') ? e.checked : e.value;
set(e.id, sett, val);
console.log(`${e.id} set to ${val}`);
}
try {
localStorage.setItem('wledUiCfg', JSON.stringify(sett));
gId('lssuc').style.display = "inline";
} catch (e) {
gId('lssuc').style.display = "none";
gId('lserr').style.display = "inline";
gId('lserr').innerHTML = "⚠ Settings JSON saving failed. (" + e + ")";
}
}
function cLS()
{
localStorage.removeItem('wledP');
localStorage.removeItem('wledPmt');
localStorage.removeItem('wledPalx');
showToast("Cleared.");
}
function Save() {
SetLS();
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();
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()
{
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=3';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
function H() { window.open("https://kno.wled.ge/features/settings/#user-interface-settings"); }
function B() { window.open("/settings","_self"); }
function UI()
{
gId('idonthateyou').style.display = (gId('dm').checked) ? 'inline':'none';
var f = gId('theme_base');
if (f) f.value = (gId('dm').checked) ? 'light':'dark';
}
// random BG image
function setRandomBg() {
if (gId("theme_bg_random").checked) {
gId("theme_bg_url").value = "https://picsum.photos/1920/1080";
} else {
gId("theme_bg_url").value = "";
}
}
function checkRandomBg() {
if (gId("theme_bg_url").value === "https://picsum.photos/1920/1080") {
gId("theme_bg_random").checked = true;
} else {
gId("theme_bg_random").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>
<button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button><br>
<span id="lssuc" style="color:green; display:none">✔ Local UI settings saved!</span>
<span id="lserr" style="color:red; display:none">⚠ Could not access local storage. Make sure it is enabled in your browser.</span><hr>
</div>
<h2>Web Setup</h2>
Server description: <input type="text" name="DS" maxlength="32"><br>
Sync button toggles both send and receive: <input type="checkbox" name="ST"><br>
<div id="theme_options" style="display:none">
<div id="NoSimple" class="hide">
<em style="color:#fa0;">This firmware build does not include simplified UI support.<br></em>
</div>
<div id="Simple">Enable simplified UI: <input type="checkbox" name="SU"><br></div>
<i>The following UI customization settings are unique both to the WLED device and this browser.<br>
You will need to set them again if using a different browser, device or WLED IP address.<br>
Refresh the main UI to apply changes.</i><br>
</div>
<div id="gen">Loading settings...</div>
<h3>UI Appearance</h3>
<label for="user_level">Userlevel:</label>
<select id="user_level" onchange="changeUserLevel()">
<option value="operator">User</option>
<option value="expert">Expert</option>
</select>
<br>
<span class="l"></span>: <input type="checkbox" id="comp_labels" class="agi cb"><br>
<span class="l"></span>: <input type="checkbox" id="comp_pcmbot" class="agi cb"><br>
<span class="l"></span>: <input type="checkbox" id="comp_pid" class="agi cb"><br>
<span class="l"></span>: <input type="checkbox" id="comp_seglen" class="agi cb"><br>
<div id="theme_options_2" style="display:none">
<span class="l"></span>: <input type="checkbox" id="comp_segpwr" class="agi cb"><br>
<span class="l"></span>: <input type="checkbox" id="comp_segexp" class="agi cb"><br>
I hate dark mode: <input type="checkbox" id="dm" onchange="UI()"><br>
<span id="idonthateyou" style="display:none"><i>Why would you? </i>🥺<br></span>
<span class="l"></span>: <input type="number" min=0.0 max=1.0 step=0.01 id="theme_alpha_tab" class="agi"><br>
<span class="l"></span>: <input type="number" min=0.0 max=1.0 step=0.01 id="theme_alpha_bg" class="agi"><br>
<span class="l"></span>: <input type="text" id="theme_color_bg" maxlength="9" class="agi"><br>
<span class="l">BG image URL</span>: <input type="text" id="theme_bg_url" class="agi" oninput="checkRandomBg()"><br>
<span class="l">Random BG image</span>: <input type="checkbox" id="theme_bg_random" class="agi cb" onchange="setRandomBg()"><br>
<input id="theme_base" class="agi" style="display:none">
<span class="l"></span>: <input type="checkbox" id="comp_css" class="agi cb"><br>
<div id="skin">Custom CSS: <input type="file" name="data" accept=".css"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/skin.css');"><br></div>
</div>
<div id="holidays" style="display:none">
<span class="l"></span>: <input type="checkbox" id="comp_hdays" class="agi cb"><br>
<div id="holidays">Holidays: <input type="file" name="data2" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data2,'/holidays.json');"><br></div>
</div>
<hr><button type="button" onclick="cLS()">Clear local storage</button>
<hr><button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button>
<div id="if-checkbox">
<input type="checkbox" id="password-checkbox" onchange="togglePasswordVisibility()">Developer</input>
</div>
<div id="password-input" style="display:none;">
<input type="password" id="password" placeholder="Enter password">
<input type="checkbox" id="confirm-password-checkbox" onchange="toggleVisibility()">Show Setup</input>
<div id="password-error"></div> <!-- Neue div für die Fehlermeldung -->
</div>
</form>
<script>
var password = "1234"; // Replace with your own password
var hideUsermodSetup = true;
function togglePasswordVisibility() {
var checkbox = document.getElementById("password-checkbox");
var passwordInput = document.getElementById("password-input");
if (checkbox.checked) {
passwordInput.style.display = "block";
} else {
passwordInput.style.display = "none";
}
}
function resetErrorMessage() {
var errorMessageElement = document.getElementById("password-error");
if (errorMessageElement) {
errorMessageElement.style.display = "none"; // hide the error message
errorMessageElement.innerHTML = ""; // reset the error message text
}
}
function toggleVisibility() {
var passwordInput = document.getElementById("password-input");
var confirmPasswordCheckbox = document.getElementById("confirm-password-checkbox");
var usermodSetup = document.getElementById("theme_options");
var usermodSetup_2 = document.getElementById("theme_options_2");
var usermodSetup_3 = document.getElementById("holidays");
var passwordError = document.getElementById("password-error");
var errorMessage = "Incorrect password, please try again.";
var userLevel = document.getElementById("user_level").value;
if (confirmPasswordCheckbox.checked && document.getElementById("password").value == password) {
hideUsermodSetup = false;
usermodSetup.style.display = "block";
usermodSetup_2.style.display = "block";
usermodSetup_3.style.display = "block";
} else {
hideUsermodSetup = true;
document.getElementById("password").value = ""; // Passwortfeld zurücksetzen
passwordError.innerHTML = "";
if (confirmPasswordCheckbox.checked) {
document.getElementById("confirm-password-checkbox").checked = false;
alert(errorMessage);
}
}
if (hideUsermodSetup) {
usermodSetup.style.display = "none";
usermodSetup_2.style.display = "none";
usermodSetup_3.style.display = "none";
document.getElementById("password-checkbox").checked = false; // Checkbox "Delevoper" zurücksetzen
passwordInput.style.display = "none";
document.getElementById("user_level").value = "operator";
}
}
function changeUserLevel() {
var userLevel = document.getElementById("user_level").value;
var holidaysDiv = document.getElementById("holidays");
var themeOptionsDiv = document.getElementById("theme_options");
var themeOptionsDiv_2 = document.getElementById("theme_options_2");
if (userLevel == "operator") {
holidaysDiv.style.display = "none";
themeOptionsDiv.style.display = "none";
themeOptionsDiv_2.style.display= "none";
} else if (userLevel == "expert") {
var password = '';
var dialog = document.createElement("dialog");
dialog.innerHTML = "<label>Expert Password:</label><input type='password' id='password_input'/><button id='password_button'>Submit</button>";
document.body.appendChild(dialog);
document.getElementById('password_button').addEventListener('click', function() {
password = document.getElementById('password_input').value;
if (password === "expert") {
holidaysDiv.style.display = "block";
themeOptionsDiv.style.display = "none";
themeOptionsDiv_2.style.display = "block";
dialog.close();
} else {
alert("False Password. Please do it again.");
}
});
dialog.showModal();
}
}
</script>
</body>
</html>