Use sequential loading and requests for all UI resources (#5013)
* use sequential loading for all UI resources - load common.js and style.css sequentially for all config pages - restrict all requrests in index.js to single connection - retry more than once if requests fail - incremental timeouts to make them faster and still more robust - bugfix in connectWs() - on page load, presets are loaded from localStorage if controller was not rebooted - remove hiding of segment freeze button when not collapsed
This commit is contained in:
@@ -51,6 +51,38 @@ function tooltip(cont=null) {
|
||||
});
|
||||
});
|
||||
};
|
||||
// sequential loading of external resources (JS or CSS) with retry, calls init() when done
|
||||
function loadResources(files, init) {
|
||||
let i = 0;
|
||||
const loadNext = () => {
|
||||
if (i >= files.length) {
|
||||
if (init) {
|
||||
d.documentElement.style.visibility = 'visible'; // make page visible after all files are loaded if it was hidden (prevent ugly display)
|
||||
d.readyState === 'complete' ? init() : window.addEventListener('load', init);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const file = files[i++];
|
||||
const isCSS = file.endsWith('.css');
|
||||
const el = d.createElement(isCSS ? 'link' : 'script');
|
||||
if (isCSS) {
|
||||
el.rel = 'stylesheet';
|
||||
el.href = file;
|
||||
const st = d.head.querySelector('style');
|
||||
if (st) d.head.insertBefore(el, st); // insert before any <style> to allow overrides
|
||||
else d.head.appendChild(el);
|
||||
} else {
|
||||
el.src = file;
|
||||
d.head.appendChild(el);
|
||||
}
|
||||
el.onload = () => { loadNext(); };
|
||||
el.onerror = () => {
|
||||
i--; // load this file again
|
||||
setTimeout(loadNext, 100);
|
||||
};
|
||||
};
|
||||
loadNext();
|
||||
}
|
||||
// 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");
|
||||
@@ -116,21 +148,22 @@ function uploadFile(fileObj, name) {
|
||||
fileObj.value = '';
|
||||
return false;
|
||||
}
|
||||
// connect to WebSocket, use parent WS or open new
|
||||
// connect to WebSocket, use parent WS or open new, callback function gets passed the new WS object
|
||||
function connectWs(onOpen) {
|
||||
try {
|
||||
if (top.window.ws && top.window.ws.readyState === WebSocket.OPEN) {
|
||||
if (onOpen) onOpen();
|
||||
return top.window.ws;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
getLoc(); // ensure globals (loc, locip, locproto) are up to date
|
||||
let url = loc ? getURL('/ws').replace("http","ws") : "ws://"+window.location.hostname+"/ws";
|
||||
let ws = new WebSocket(url);
|
||||
ws.binaryType = "arraybuffer";
|
||||
if (onOpen) { ws.onopen = onOpen; }
|
||||
try { top.window.ws = ws; } catch (e) {} // store in parent for reuse
|
||||
let ws;
|
||||
try { ws = top.window.ws;} catch (e) {}
|
||||
// reuse if open
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
if (onOpen) onOpen(ws);
|
||||
} else {
|
||||
// create new ws connection
|
||||
getLoc(); // ensure globals are up to date
|
||||
let url = loc ? getURL('/ws').replace("http", "ws")
|
||||
: "ws://" + window.location.hostname + "/ws";
|
||||
ws = new WebSocket(url);
|
||||
ws.binaryType = "arraybuffer";
|
||||
if (onOpen) ws.onopen = () => onOpen(ws);
|
||||
}
|
||||
return ws;
|
||||
}
|
||||
|
||||
|
||||
@@ -1476,7 +1476,7 @@ dialog {
|
||||
.expanded {
|
||||
display: inline-block !important;
|
||||
}
|
||||
.hide, .expanded .segin.hide, .expanded .presin.hide, .expanded .sbs.hide, .expanded .frz, .expanded .g-icon {
|
||||
.hide, .expanded .segin.hide, .expanded .presin.hide, .expanded .sbs.hide, .expanded .g-icon {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,12 @@ var simplifiedUI = false;
|
||||
var tr = 7;
|
||||
var d = document;
|
||||
const ranges = RangeTouch.setup('input[type="range"]', {});
|
||||
var retry = false;
|
||||
var palettesData;
|
||||
var fxdata = [];
|
||||
var pJson = {}, eJson = {}, lJson = {};
|
||||
var plJson = {}; // array of playlists
|
||||
var pN = "", pI = 0, pNum = 0;
|
||||
var pmt = 1, pmtLS = 0, pmtLast = 0;
|
||||
var pmt = 1, pmtLS = 0;
|
||||
var lastinfo = {};
|
||||
var isM = false, mw = 0, mh=0;
|
||||
var ws, wsRpt=0;
|
||||
@@ -200,19 +199,17 @@ function loadBg() {
|
||||
});
|
||||
}
|
||||
|
||||
function loadSkinCSS(cId)
|
||||
{
|
||||
if (!gId(cId)) // check if element exists
|
||||
{
|
||||
var h = d.getElementsByTagName('head')[0];
|
||||
var l = d.createElement('link');
|
||||
l.id = cId;
|
||||
l.rel = 'stylesheet';
|
||||
l.type = 'text/css';
|
||||
function loadSkinCSS(cId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (gId(cId)) return resolve();
|
||||
const l = d.createElement('link');
|
||||
l.id = cId;
|
||||
l.rel = 'stylesheet';
|
||||
l.href = getURL('/skin.css');
|
||||
l.media = 'all';
|
||||
h.appendChild(l);
|
||||
}
|
||||
l.onload = resolve;
|
||||
l.onerror = reject;
|
||||
d.head.appendChild(l);
|
||||
});
|
||||
}
|
||||
|
||||
function getURL(path) {
|
||||
@@ -278,19 +275,23 @@ function onLoad()
|
||||
cpick.on("color:change", () => {updatePSliders()});
|
||||
pmtLS = localStorage.getItem('wledPmt');
|
||||
|
||||
// Load initial data
|
||||
loadPalettes(()=>{
|
||||
// fill effect extra data array
|
||||
loadFXData(()=>{
|
||||
// load and populate effects
|
||||
setTimeout(()=>{loadFX(()=>{
|
||||
loadPalettesData(()=>{
|
||||
requestJson();// will load presets and create WS
|
||||
if (cfg.comp.css) setTimeout(()=>{loadSkinCSS('skinCss')},50);
|
||||
});
|
||||
})},50);
|
||||
});
|
||||
});
|
||||
// Load initial data sequentially, no parallel requests to avoid "503" errors when heap is low (slower but much more reliable)
|
||||
(async ()=>{
|
||||
try {
|
||||
await loadPalettes(); // loads base palettes and builds #pallist (safe first)
|
||||
await loadFXData(); // loads fx data
|
||||
await loadFX(); // populates effect list
|
||||
await requestJson(); // updates info variables
|
||||
await loadPalettesData(); // fills palettesData[] for previews
|
||||
populatePalettes(); // repopulate with custom palettes now that cpalcount is known
|
||||
if(pmt == pmtLS) populatePresets(true); // load presets from localStorage if signature matches (i.e. no device reboot)
|
||||
else await loadPresets(); // load and populate presets
|
||||
if (cfg.comp.css) await loadSkinCSS('skinCss');
|
||||
if (!ws) makeWS();
|
||||
} catch(e) {
|
||||
showToast("Init failed: " + e, true);
|
||||
}
|
||||
})();
|
||||
resetUtil();
|
||||
|
||||
d.addEventListener("visibilitychange", handleVisibilityChange, false);
|
||||
@@ -448,7 +449,7 @@ function presetError(empty)
|
||||
if (bckstr.length > 10) hasBackup = true;
|
||||
} catch (e) {}
|
||||
|
||||
var cn = `<div class="pres c" style="padding:8px;margin-bottom:8px;${empty?'':'cursor:pointer;'}" ${empty?'':'onclick="pmtLast=0;loadPresets();"'}>`;
|
||||
var cn = `<div class="pres c" style="padding:8px;margin-bottom:8px;${empty?'':'cursor:pointer;'}" ${empty?'':'onclick="loadPresets();"'}>`;
|
||||
if (empty)
|
||||
cn += `You have no presets yet!`;
|
||||
else
|
||||
@@ -481,123 +482,81 @@ function restore(txt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function loadPresets(callback = null)
|
||||
{
|
||||
// 1st boot (because there is a callback)
|
||||
if (callback && pmt == pmtLS && pmt > 0) {
|
||||
// we have a copy of the presets in local storage and don't need to fetch another one
|
||||
populatePresets(true);
|
||||
pmtLast = pmt;
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// afterwards
|
||||
if (!callback && pmt == pmtLast) return;
|
||||
|
||||
fetch(getURL('/presets.json'), {
|
||||
method: 'get'
|
||||
})
|
||||
.then(res => {
|
||||
if (res.status=="404") return {"0":{}};
|
||||
//if (!res.ok) showErrorToast();
|
||||
return res.json();
|
||||
})
|
||||
.then(json => {
|
||||
pJson = json;
|
||||
pmtLast = pmt;
|
||||
populatePresets();
|
||||
})
|
||||
.catch((e)=>{
|
||||
//showToast(e, true);
|
||||
presetError(false);
|
||||
})
|
||||
.finally(()=>{
|
||||
if (callback) setTimeout(callback,99);
|
||||
async function loadPresets() {
|
||||
return new Promise((resolve) => {
|
||||
fetch(getURL('/presets.json'), {method: 'get'})
|
||||
.then(res => res.status=="404" ? {"0":{}} : res.json())
|
||||
.then(json => {
|
||||
pJson = json;
|
||||
populatePresets();
|
||||
resolve();
|
||||
})
|
||||
.catch(() => {
|
||||
presetError(false);
|
||||
resolve();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function loadPalettes(callback = null)
|
||||
{
|
||||
fetch(getURL('/json/palettes'), {
|
||||
method: 'get'
|
||||
})
|
||||
.then((res)=>{
|
||||
if (!res.ok) showErrorToast();
|
||||
return res.json();
|
||||
})
|
||||
.then((json)=>{
|
||||
lJson = Object.entries(json);
|
||||
populatePalettes();
|
||||
retry = false;
|
||||
})
|
||||
.catch((e)=>{
|
||||
if (!retry) {
|
||||
retry = true;
|
||||
setTimeout(loadPalettes, 500); // retry
|
||||
}
|
||||
showToast(e, true);
|
||||
})
|
||||
.finally(()=>{
|
||||
if (callback) callback();
|
||||
updateUI();
|
||||
async function loadPalettes(retry=0) {
|
||||
return new Promise((resolve) => {
|
||||
fetch(getURL('/json/palettes'), {method: 'get'})
|
||||
.then(res => res.ok ? res.json() : Promise.reject())
|
||||
.then(json => {
|
||||
lJson = Object.entries(json);
|
||||
populatePalettes();
|
||||
resolve();
|
||||
})
|
||||
.catch((e) => {
|
||||
if (retry<5) {
|
||||
setTimeout(() => loadPalettes(retry+1).then(resolve), 100);
|
||||
} else {
|
||||
showToast(e, true);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadFX(callback = null)
|
||||
{
|
||||
fetch(getURL('/json/effects'), {
|
||||
method: 'get'
|
||||
})
|
||||
.then((res)=>{
|
||||
if (!res.ok) showErrorToast();
|
||||
return res.json();
|
||||
})
|
||||
.then((json)=>{
|
||||
eJson = Object.entries(json);
|
||||
populateEffects();
|
||||
retry = false;
|
||||
})
|
||||
.catch((e)=>{
|
||||
if (!retry) {
|
||||
retry = true;
|
||||
setTimeout(loadFX, 500); // retry
|
||||
}
|
||||
showToast(e, true);
|
||||
})
|
||||
.finally(()=>{
|
||||
if (callback) callback();
|
||||
updateUI();
|
||||
async function loadFX(retry=0) {
|
||||
return new Promise((resolve) => {
|
||||
fetch(getURL('/json/effects'), {method: 'get'})
|
||||
.then(res => res.ok ? res.json() : Promise.reject())
|
||||
.then(json => {
|
||||
eJson = Object.entries(json);
|
||||
populateEffects();
|
||||
resolve();
|
||||
})
|
||||
.catch((e) => {
|
||||
if (retry<5) {
|
||||
setTimeout(() => loadFX(retry+1).then(resolve), 100);
|
||||
} else {
|
||||
showToast(e, true);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadFXData(callback = null)
|
||||
{
|
||||
fetch(getURL('/json/fxdata'), {
|
||||
method: 'get'
|
||||
})
|
||||
.then((res)=>{
|
||||
if (!res.ok) showErrorToast();
|
||||
return res.json();
|
||||
})
|
||||
.then((json)=>{
|
||||
fxdata = json||[];
|
||||
// add default value for Solid
|
||||
fxdata.shift()
|
||||
fxdata.unshift(";!;");
|
||||
retry = false;
|
||||
})
|
||||
.catch((e)=>{
|
||||
fxdata = [];
|
||||
if (!retry) {
|
||||
retry = true;
|
||||
setTimeout(()=>{loadFXData(loadFX);}, 500); // retry
|
||||
}
|
||||
showToast(e, true);
|
||||
})
|
||||
.finally(()=>{
|
||||
if (callback) callback();
|
||||
updateUI();
|
||||
async function loadFXData(retry=0) {
|
||||
return new Promise((resolve) => {
|
||||
fetch(getURL('/json/fxdata'), {method: 'get'})
|
||||
.then(res => res.ok ? res.json() : Promise.reject())
|
||||
.then(json => {
|
||||
fxdata = json||[];
|
||||
fxdata.shift();
|
||||
fxdata.unshift(";!;");
|
||||
resolve();
|
||||
})
|
||||
.catch((e) => {
|
||||
fxdata = [];
|
||||
if (retry<5) {
|
||||
setTimeout(() => loadFXData(retry+1).then(resolve), 100);
|
||||
} else {
|
||||
showToast(e, true);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -619,7 +578,7 @@ function populateQL()
|
||||
function populatePresets(fromls)
|
||||
{
|
||||
if (fromls) pJson = JSON.parse(localStorage.getItem("wledP"));
|
||||
if (!pJson) {setTimeout(loadPresets,250); return;}
|
||||
if (!pJson) {loadPresets(); return;} // note: no await as this is a fallback that should not be needed as init function fetches pJson
|
||||
delete pJson["0"];
|
||||
var cn = "";
|
||||
var arr = Object.entries(pJson).sort(cmpP);
|
||||
@@ -701,10 +660,10 @@ function parseInfo(i) {
|
||||
//var setInnerHTML = function(elm, html) {
|
||||
// elm.innerHTML = html;
|
||||
// Array.from(elm.querySelectorAll("script")).forEach( oldScript => {
|
||||
// const newScript = document.createElement("script");
|
||||
// const newScript = d.createElement("script");
|
||||
// Array.from(oldScript.attributes)
|
||||
// .forEach( attr => newScript.setAttribute(attr.name, attr.value) );
|
||||
// newScript.appendChild(document.createTextNode(oldScript.innerHTML));
|
||||
// newScript.appendChild(d.createTextNode(oldScript.innerHTML));
|
||||
// oldScript.parentNode.replaceChild(newScript, oldScript);
|
||||
// });
|
||||
//}
|
||||
@@ -906,6 +865,7 @@ function populateSegments(s)
|
||||
gId("segcont").classList.remove("hide");
|
||||
let noNewSegs = (lowestUnused >= maxSeg);
|
||||
resetUtil(noNewSegs);
|
||||
if (segCount === 0) return; // no segments to populate
|
||||
for (var i = 0; i <= lSeg; i++) {
|
||||
if (!gId(`seg${i}`)) continue;
|
||||
updateLen(i);
|
||||
@@ -1437,7 +1397,7 @@ function makeWS() {
|
||||
};
|
||||
ws.onclose = (e)=>{
|
||||
gId('connind').style.backgroundColor = "var(--c-r)";
|
||||
if (wsRpt++ < 5) setTimeout(makeWS,1500); // retry WS connection
|
||||
if (wsRpt++ < 10) setTimeout(makeWS,wsRpt * 200); // retry WS connection
|
||||
ws = null;
|
||||
}
|
||||
ws.onopen = (e)=>{
|
||||
@@ -1470,6 +1430,7 @@ function readState(s,command=false)
|
||||
|
||||
populateSegments(s);
|
||||
hasRGB = hasWhite = hasCCT = has2D = false;
|
||||
segLmax = 0; // reset max selected segment length
|
||||
let i = {};
|
||||
// determine light capabilities from selected segments
|
||||
for (let seg of (s.seg||[])) {
|
||||
@@ -1709,77 +1670,68 @@ function setEffectParameters(idx)
|
||||
|
||||
var jsonTimeout;
|
||||
var reqsLegal = false;
|
||||
async function requestJson(command=null, retry=0) {
|
||||
return new Promise((resolve, reject) => {
|
||||
gId('connind').style.backgroundColor = "var(--c-y)";
|
||||
if (command && !reqsLegal) {resolve(); return;}
|
||||
if (!jsonTimeout) jsonTimeout = setTimeout(()=>{if (ws) ws.close(); ws=null; showErrorToast()}, 3000);
|
||||
|
||||
function requestJson(command=null)
|
||||
{
|
||||
gId('connind').style.backgroundColor = "var(--c-y)";
|
||||
if (command && !reqsLegal) return; // stop post requests from chrome onchange event on page restore
|
||||
if (!jsonTimeout) jsonTimeout = setTimeout(()=>{if (ws) ws.close(); ws=null; showErrorToast()}, 3000);
|
||||
var req = null;
|
||||
var useWs = (ws && ws.readyState === WebSocket.OPEN);
|
||||
var type = command ? 'post':'get';
|
||||
if (command) {
|
||||
command.v = true; // force complete /json/si API response
|
||||
command.time = Math.floor(Date.now() / 1000);
|
||||
var t = gId('tt');
|
||||
if (t.validity.valid && command.transition==null) {
|
||||
var tn = parseInt(t.value*10);
|
||||
if (tn != tr) command.transition = tn;
|
||||
var useWs = (ws && ws.readyState === WebSocket.OPEN);
|
||||
var req = null;
|
||||
if (command) {
|
||||
command.v = true;
|
||||
command.time = Math.floor(Date.now() / 1000);
|
||||
var t = gId('tt');
|
||||
if (t && t.validity.valid && command.transition==null) {
|
||||
var tn = parseInt(t.value*10);
|
||||
if (tn != tr) command.transition = tn;
|
||||
}
|
||||
req = JSON.stringify(command);
|
||||
if (req.length > 1340) useWs = false;
|
||||
if (req.length > 500 && lastinfo && lastinfo.arch == "esp8266") useWs = false;
|
||||
}
|
||||
//command.bs = parseInt(gId('bs').value);
|
||||
req = JSON.stringify(command);
|
||||
if (req.length > 1340) useWs = false; // do not send very long requests over websocket
|
||||
if (req.length > 500 && lastinfo && lastinfo.arch == "esp8266") useWs = false; // esp8266 can only handle 500 bytes
|
||||
};
|
||||
|
||||
if (useWs) {
|
||||
ws.send(req?req:'{"v":true}');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(getURL('/json/si'), {
|
||||
method: type,
|
||||
headers: {"Content-Type": "application/json; charset=UTF-8"},
|
||||
body: req
|
||||
})
|
||||
.then(res => {
|
||||
clearTimeout(jsonTimeout);
|
||||
jsonTimeout = null;
|
||||
if (!res.ok) showErrorToast();
|
||||
return res.json();
|
||||
})
|
||||
.then(json => {
|
||||
lastUpdate = new Date();
|
||||
clearErrorToast(3000);
|
||||
gId('connind').style.backgroundColor = "var(--c-g)";
|
||||
if (!json) { showToast('Empty response', true); return; }
|
||||
if (json.success) return;
|
||||
if (json.info) {
|
||||
let i = json.info;
|
||||
parseInfo(i);
|
||||
populatePalettes(i);
|
||||
if (isInfo) populateInfo(i);
|
||||
if (simplifiedUI) simplifyUI();
|
||||
if (useWs) {
|
||||
ws.send(req?req:'{"v":true}');
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
var s = json.state ? json.state : json;
|
||||
readState(s);
|
||||
|
||||
//load presets and open websocket sequentially
|
||||
if (!pJson || isEmpty(pJson)) setTimeout(()=>{
|
||||
loadPresets(()=>{
|
||||
wsRpt = 0;
|
||||
if (!(ws && ws.readyState === WebSocket.OPEN)) makeWS();
|
||||
});
|
||||
},25);
|
||||
reqsLegal = true;
|
||||
retry = false;
|
||||
})
|
||||
.catch((e)=>{
|
||||
if (!retry) {
|
||||
retry = true;
|
||||
setTimeout(requestJson,500);
|
||||
}
|
||||
showToast(e, true);
|
||||
fetch(getURL('/json/si'), {
|
||||
method: command ? 'post' : 'get',
|
||||
headers: {"Content-Type": "application/json; charset=UTF-8"},
|
||||
body: req
|
||||
})
|
||||
.then(res => {
|
||||
clearTimeout(jsonTimeout);
|
||||
jsonTimeout = null;
|
||||
return res.ok ? res.json() : Promise.reject();
|
||||
})
|
||||
.then(json => {
|
||||
lastUpdate = new Date();
|
||||
clearErrorToast(3000);
|
||||
gId('connind').style.backgroundColor = "var(--c-g)";
|
||||
if (!json) { showToast('Empty response', true); resolve(); return; }
|
||||
if (json.success) {resolve(); return;}
|
||||
if (json.info) {
|
||||
parseInfo(json.info);
|
||||
if (isInfo) populateInfo(json.info);
|
||||
if (simplifiedUI) simplifyUI();
|
||||
}
|
||||
var s = json.state ? json.state : json;
|
||||
readState(s);
|
||||
|
||||
reqsLegal = true;
|
||||
resolve();
|
||||
})
|
||||
.catch((e)=>{
|
||||
if (retry<10) {
|
||||
setTimeout(() => requestJson(command,retry+1).then(resolve).catch(reject), retry*50);
|
||||
} else {
|
||||
showToast(e, true);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2554,7 +2506,7 @@ function saveP(i,pl)
|
||||
}
|
||||
populatePresets();
|
||||
resetPUtil();
|
||||
setTimeout(()=>{pmtLast=0; loadPresets();}, 750); // force reloading of presets
|
||||
setTimeout(()=>{loadPresets();}, 750); // force reloading of presets
|
||||
}
|
||||
|
||||
function testPl(i,bt) {
|
||||
@@ -2820,56 +2772,51 @@ function rSegs()
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
function loadPalettesData(callback = null)
|
||||
{
|
||||
if (palettesData) return;
|
||||
const lsKey = "wledPalx";
|
||||
var lsPalData = localStorage.getItem(lsKey);
|
||||
if (lsPalData) {
|
||||
try {
|
||||
var d = JSON.parse(lsPalData);
|
||||
if (d && d.vid == d.vid) {
|
||||
palettesData = d.p;
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
function loadPalettesData() {
|
||||
return new Promise((resolve) => {
|
||||
if (palettesData) return resolve(); // already loaded
|
||||
var lsPalData = localStorage.getItem("wledPalx");
|
||||
if (lsPalData) {
|
||||
try {
|
||||
var d = JSON.parse(lsPalData);
|
||||
if (d && d.vid == lastinfo.vid) {
|
||||
palettesData = d.p;
|
||||
redrawPalPrev();
|
||||
return resolve();
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
palettesData = {};
|
||||
getPalettesData(0, ()=>{
|
||||
localStorage.setItem(lsKey, JSON.stringify({
|
||||
p: palettesData,
|
||||
vid: lastinfo.vid
|
||||
}));
|
||||
redrawPalPrev();
|
||||
if (callback) setTimeout(callback, 99);
|
||||
palettesData = {};
|
||||
getPalettesData(0, () => {
|
||||
localStorage.setItem("wledPalx", JSON.stringify({
|
||||
p: palettesData,
|
||||
vid: lastinfo.vid
|
||||
}));
|
||||
redrawPalPrev();
|
||||
setTimeout(resolve, 99); // delay optional
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getPalettesData(page, callback)
|
||||
{
|
||||
fetch(getURL(`/json/palx?page=${page}`), {
|
||||
method: 'get'
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) showErrorToast();
|
||||
return res.json();
|
||||
})
|
||||
function getPalettesData(page, callback, retry=0) {
|
||||
fetch(getURL(`/json/palx?page=${page}`), {method: 'get'})
|
||||
.then(res => res.ok ? res.json() : Promise.reject())
|
||||
.then(json => {
|
||||
retry = false;
|
||||
palettesData = Object.assign({}, palettesData, json.p);
|
||||
if (page < json.m) setTimeout(()=>{ getPalettesData(page + 1, callback); }, 75);
|
||||
else callback();
|
||||
})
|
||||
.catch((error)=>{
|
||||
if (!retry) {
|
||||
retry = true;
|
||||
setTimeout(()=>{getPalettesData(page,callback);}, 500); // retry
|
||||
if (retry<5) {
|
||||
setTimeout(()=>{getPalettesData(page,callback,retry+1);}, 100);
|
||||
} else {
|
||||
showToast(error, true);
|
||||
callback();
|
||||
}
|
||||
showToast(error, true);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
function hideModes(txt)
|
||||
{
|
||||
@@ -2971,7 +2918,7 @@ function filterFocus(e) {
|
||||
}
|
||||
if (e.type === "blur") {
|
||||
setTimeout(() => {
|
||||
if (e.target === document.activeElement && document.hasFocus()) return;
|
||||
if (e.target === d.activeElement && d.hasFocus()) return;
|
||||
// do not hide if filter is active
|
||||
if (!c) {
|
||||
// compute sticky top
|
||||
@@ -3218,7 +3165,7 @@ function simplifyUI() {
|
||||
// Create dropdown dialog
|
||||
function createDropdown(id, buttonText, dialogElements = null) {
|
||||
// Create dropdown dialog
|
||||
const dialog = document.createElement("dialog");
|
||||
const dialog = d.createElement("dialog");
|
||||
// Move every dialogElement to the dropdown dialog or if none are given, move all children of the element with the given id
|
||||
if (dialogElements) {
|
||||
dialogElements.forEach((e) => {
|
||||
@@ -3231,7 +3178,7 @@ function simplifyUI() {
|
||||
}
|
||||
|
||||
// Create button for the dropdown
|
||||
const btn = document.createElement("button");
|
||||
const btn = d.createElement("button");
|
||||
btn.id = id + "btn";
|
||||
btn.classList.add("btn");
|
||||
btn.innerText = buttonText;
|
||||
@@ -3279,7 +3226,7 @@ function simplifyUI() {
|
||||
|
||||
// Hide palette label
|
||||
gId("pall").style.display = "none";
|
||||
gId("Colors").insertBefore(document.createElement("br"), gId("pall"));
|
||||
gId("Colors").insertBefore(d.createElement("br"), gId("pall"));
|
||||
// Hide effect label
|
||||
gId("modeLabel").style.display = "none";
|
||||
|
||||
@@ -3291,7 +3238,7 @@ function simplifyUI() {
|
||||
|
||||
// Hide bottom bar
|
||||
gId("bot").style.display = "none";
|
||||
document.documentElement.style.setProperty('--bh', '0px');
|
||||
d.documentElement.style.setProperty('--bh', '0px');
|
||||
|
||||
// Hide other tabs
|
||||
gId("Effects").style.display = "none";
|
||||
|
||||
@@ -17,8 +17,15 @@
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
// load common.js with retry on error
|
||||
(function common() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => S();
|
||||
l.onerror = () => setTimeout(common, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
var ws;
|
||||
var tmout = null;
|
||||
var c;
|
||||
@@ -31,7 +38,7 @@
|
||||
ctx.fillRect(Math.round((i - start) * w / skip), 0, Math.ceil(w), c.height);
|
||||
}
|
||||
}
|
||||
function update() { // via HTTP (/json/live)
|
||||
function update(retry=0) { // via HTTP (/json/live)
|
||||
if (d.hidden) {
|
||||
clearTimeout(tmout);
|
||||
tmout = setTimeout(update, 250);
|
||||
@@ -53,7 +60,7 @@
|
||||
.catch((error)=>{
|
||||
//console.error("Peek HTTP error:",error);
|
||||
clearTimeout(tmout);
|
||||
tmout = setTimeout(update, 2500);
|
||||
if (retry<5) tmout = setTimeout(() => update(retry+1), 2500); // stop endlessly bugging the ESP if resource is not available
|
||||
})
|
||||
}
|
||||
function S() { // Startup function (onload)
|
||||
@@ -62,10 +69,7 @@
|
||||
if (window.location.href.indexOf("?ws") == -1) {update(); return;}
|
||||
|
||||
// Initialize WebSocket connection
|
||||
ws = connectWs(function () {
|
||||
//console.info("Peek WS open");
|
||||
ws.send('{"lv":true}');
|
||||
});
|
||||
ws = connectWs(ws => ws.send('{"lv":true}'));
|
||||
ws.addEventListener('message', (e) => {
|
||||
try {
|
||||
if (toString.call(e.data) === '[object ArrayBuffer]') {
|
||||
@@ -81,7 +85,7 @@
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<canvas id="canv"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
@@ -10,11 +10,18 @@
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<script src="common.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canv"></canvas>
|
||||
<script>
|
||||
// load common.js with retry on error
|
||||
(function common() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => S();
|
||||
l.onerror = () => setTimeout(common, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
var c = document.getElementById('canv');
|
||||
var leds = "";
|
||||
var throttled = false;
|
||||
@@ -22,36 +29,35 @@
|
||||
c.width = window.innerWidth * 0.98; //remove scroll bars
|
||||
c.height = window.innerHeight * 0.98; //remove scroll bars
|
||||
}
|
||||
setCanvas();
|
||||
// Check for canvas support
|
||||
var ctx = c.getContext('2d');
|
||||
if (ctx) { // Access the rendering context
|
||||
// use parent WS or open new
|
||||
var ws = connectWs(()=>{
|
||||
ws.send('{"lv":true}');
|
||||
});
|
||||
ws.addEventListener('message',(e)=>{
|
||||
try {
|
||||
if (toString.call(e.data) === '[object ArrayBuffer]') {
|
||||
let leds = new Uint8Array(e.data);
|
||||
if (leds[0] != 76 || leds[1] != 2 || !ctx) return; //'L', set in ws.cpp
|
||||
let mW = leds[2]; // matrix width
|
||||
let mH = leds[3]; // matrix height
|
||||
let pPL = Math.min(c.width / mW, c.height / mH); // pixels per LED (width of circle)
|
||||
let lOf = Math.floor((c.width - pPL*mW)/2); //left offset (to center matrix)
|
||||
var i = 4;
|
||||
for (y=0.5;y<mH;y++) for (x=0.5; x<mW; x++) {
|
||||
ctx.fillStyle = `rgb(${leds[i]},${leds[i+1]},${leds[i+2]})`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x*pPL+lOf, y*pPL, pPL*0.4, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
i+=3;
|
||||
function S() { // Startup function (onload)
|
||||
setCanvas();
|
||||
// Check for canvas support
|
||||
var ctx = c.getContext('2d');
|
||||
if (ctx) { // Access the rendering context
|
||||
ws = connectWs(ws => ws.send('{"lv":true}')); // use parent WS or open new
|
||||
ws.addEventListener('message',(e)=>{
|
||||
try {
|
||||
if (toString.call(e.data) === '[object ArrayBuffer]') {
|
||||
let leds = new Uint8Array(e.data);
|
||||
if (leds[0] != 76 || leds[1] != 2 || !ctx) return; //'L', set in ws.cpp
|
||||
let mW = leds[2]; // matrix width
|
||||
let mH = leds[3]; // matrix height
|
||||
let pPL = Math.min(c.width / mW, c.height / mH); // pixels per LED (width of circle)
|
||||
let lOf = Math.floor((c.width - pPL*mW)/2); //left offset (to center matrix)
|
||||
var i = 4;
|
||||
for (y=0.5;y<mH;y++) for (x=0.5; x<mW; x++) {
|
||||
ctx.fillStyle = `rgb(${leds[i]},${leds[i+1]},${leds[i+2]})`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x*pPL+lOf, y*pPL, pPL*0.4, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
i+=3;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Peek WS error:",err);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Peek WS error:",err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
// window.resize event listener
|
||||
window.addEventListener('resize', (e)=>{
|
||||
|
||||
@@ -4,12 +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>WLED Settings</title>
|
||||
<script src="common.js" type="text/javascript"></script>
|
||||
<script>
|
||||
function S() {
|
||||
getLoc();
|
||||
loadJS(getURL('/settings/s.js?p=0'), false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
}
|
||||
// load common.js with retry on error
|
||||
(function loadFiles() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
// load style.css then initialize
|
||||
l.onload = () => loadResources(['style.css'], () => {
|
||||
getLoc();
|
||||
loadJS(getURL('/settings/s.js?p=0'), false);
|
||||
});
|
||||
l.onerror = () => setTimeout(loadFiles, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@import url("style.css");
|
||||
@@ -31,7 +38,7 @@
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<button type=submit id="b" onclick="window.location=getURL('/')">Back</button>
|
||||
<button type="submit" onclick="window.location=getURL('/settings/wifi')">WiFi Setup</button>
|
||||
<button type="submit" onclick="window.location=getURL('/settings/leds')">LED Preferences</button>
|
||||
|
||||
@@ -4,11 +4,20 @@
|
||||
<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" type="text/javascript"></script>
|
||||
<style> html { visibility: hidden; } </style> <!-- prevent white & ugly display while loading, unhidden in loadResources() -->
|
||||
<script>
|
||||
var maxPanels=64;
|
||||
var ctx = null;
|
||||
function fS(){d.Sf.submit();} // <button type=submit> sometimes didn't work
|
||||
// load common.js with retry on error
|
||||
(function loadFiles() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => loadResources(['style.css'], S); // load style.css then call S()
|
||||
l.onerror = () => setTimeout(loadFiles, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
|
||||
function S() {
|
||||
getLoc();
|
||||
loadJS(getURL('/settings/s.js?p=10'), false, undefined, ()=>{
|
||||
@@ -238,9 +247,8 @@ Y: <input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()"
|
||||
gId("MD").innerHTML = "Matrix Dimensions (W*H=LC): " + maxWidth + " x " + maxHeight + " = " + maxWidth * maxHeight;
|
||||
}
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="H('features/2D')">?</button></div>
|
||||
|
||||
@@ -4,8 +4,16 @@
|
||||
<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" type="text/javascript"></script>
|
||||
<style> html { visibility: hidden; } </style> <!-- prevent white & ugly display while loading, unhidden in loadResources() -->
|
||||
<script>
|
||||
// load common.js with retry on error
|
||||
(function loadFiles() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => loadResources(['style.css'], S); // load style.css then call S()
|
||||
l.onerror = () => setTimeout(loadFiles, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
function HW(){window.open("https://kno.wled.ge/interfaces/dmx-output/");}
|
||||
function GCH(num) {
|
||||
gId('dmxchannels').innerHTML += "";
|
||||
@@ -38,9 +46,8 @@
|
||||
if (loc) d.Sf.action = getURL('/settings/dmx');
|
||||
}
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="HW()">?</button></div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<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" type="text/javascript"></script>
|
||||
<style> html { visibility: hidden; } </style> <!-- prevent white & ugly display while loading, unhidden in loadResources() -->
|
||||
<script>
|
||||
var maxB=1,maxD=1,maxA=1,maxV=0,maxM=4000,maxPB=2048,maxL=1664,maxCO=5,maxBT=4; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
|
||||
var customStarts=false,startsDirty=[];
|
||||
@@ -26,6 +26,15 @@
|
||||
function numPins(t){ return Math.max(gT(t).t.length, 1); } // type length determines number of GPIO pins
|
||||
function chrID(x) { return String.fromCharCode((x<10?48:55)+x); }
|
||||
function toNum(c) { let n=c.charCodeAt(0); return (n>=48 && n<=57)?n-48:(n>=65 && n<=90)?n-55:0; } // convert char (0-9A-Z) to number (0-35)
|
||||
// load common.js with retry on error
|
||||
(function loadFiles() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => loadResources(['style.css'], S); // load style.css then call S()
|
||||
l.onerror = () => setTimeout(loadFiles, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
|
||||
function S() {
|
||||
getLoc();
|
||||
loadJS(getURL('/settings/s.js?p=2'), false, ()=>{
|
||||
@@ -846,9 +855,8 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="H('features/settings/#led-settings')">?</button></div>
|
||||
|
||||
@@ -4,8 +4,16 @@
|
||||
<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" type="text/javascript"></script>
|
||||
<style> html { visibility: hidden; } </style> <!-- prevent white & ugly display while loading, unhidden in loadResources() -->
|
||||
<script>
|
||||
// load common.js with retry on error
|
||||
(function loadFiles() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => loadResources(['style.css'], S); // load style.css then call S()
|
||||
l.onerror = () => setTimeout(loadFiles, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
function U() { window.open(getURL("/update"),"_self"); }
|
||||
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"];
|
||||
@@ -30,11 +38,8 @@
|
||||
if (loc) d.Sf.action = getURL('/settings/sec');
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@import url("style.css");
|
||||
</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="H('features/settings/#security-settings')">?</button></div>
|
||||
|
||||
@@ -4,8 +4,16 @@
|
||||
<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 src="common.js" type="text/javascript"></script>
|
||||
<style> html { visibility: hidden; } </style> <!-- prevent white & ugly display while loading, unhidden in loadResources() -->
|
||||
<script>
|
||||
// load common.js with retry on error
|
||||
(function loadFiles() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => loadResources(['style.css'], S); // load style.css then call S()
|
||||
l.onerror = () => setTimeout(loadFiles, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
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;} }
|
||||
function FC()
|
||||
@@ -43,9 +51,8 @@
|
||||
function hideDMXInput(){gId("dmxInput").style.display="none";}
|
||||
function hideNoDMXInput(){gId("dmxInputOff").style.display="none";}
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<form id="form_s" name="Sf" method="post" onsubmit="GC()">
|
||||
<div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="H('interfaces/udp-notifier/')">?</button></div>
|
||||
|
||||
@@ -4,10 +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" type="text/javascript"></script>
|
||||
<style> html { visibility: hidden; } </style> <!-- prevent white & ugly display while loading, unhidden in loadResources() -->
|
||||
<script>
|
||||
var el=false;
|
||||
var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
||||
// load common.js with retry on error
|
||||
(function loadFiles() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => loadResources(['style.css'], S); // load style.css then call S()
|
||||
l.onerror = () => setTimeout(loadFiles, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
|
||||
function S() {
|
||||
getLoc();
|
||||
loadJS(getURL('/settings/s.js?p=5'), false, ()=>{BTa();}, ()=>{
|
||||
@@ -119,9 +128,8 @@
|
||||
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";
|
||||
}
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<form id="form_s" name="Sf" method="post" onsubmit="Wd()">
|
||||
<div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="H('features/settings/#time-settings')">?</button></div>
|
||||
|
||||
@@ -4,10 +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>UI Settings</title>
|
||||
<script src="common.js" type="text/javascript"></script>
|
||||
<style> html { visibility: hidden; } </style> <!-- prevent white & ugly display while loading, unhidden in loadResources() -->
|
||||
<script>
|
||||
var initial_ds, initial_st, initial_su, oldUrl;
|
||||
var sett = null;
|
||||
// load common.js with retry on error
|
||||
(function loadFiles() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => loadResources(['style.css'], S); // load style.css then call S()
|
||||
l.onerror = () => setTimeout(loadFiles, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
|
||||
var l = {
|
||||
"comp":{
|
||||
"labels":"Show button labels",
|
||||
@@ -208,9 +217,8 @@
|
||||
gId("theme_bg_rnd").checked = false;
|
||||
}
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="H('features/settings/#user-interface-settings')">?</button></div>
|
||||
|
||||
@@ -4,12 +4,22 @@
|
||||
<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" type="text/javascript"></script>
|
||||
<style> html { visibility: hidden; } </style> <!-- prevent white & ugly display while loading, unhidden in loadResources() -->
|
||||
<script>
|
||||
var umCfg = {};
|
||||
var pins = [], pinO = [], owner;
|
||||
var urows;
|
||||
var numM = 0;
|
||||
// load common.js with retry on error
|
||||
(function loadFiles() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => loadResources(['style.css'], S); // load style.css then call S()
|
||||
l.onerror = () => setTimeout(loadFiles, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
|
||||
|
||||
function S() {
|
||||
getLoc();
|
||||
// load settings and insert values into DOM
|
||||
@@ -269,10 +279,9 @@
|
||||
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
|
||||
}
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<form id="form_s" name="Sf" method="post" onsubmit="svS(event)">
|
||||
<div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
|
||||
@@ -4,8 +4,17 @@
|
||||
<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" type="text/javascript"></script>
|
||||
<style> html { visibility: hidden; } </style> <!-- prevent white & ugly display while loading, unhidden in loadResources() -->
|
||||
<script>
|
||||
// load common.js with retry on error
|
||||
(function loadFiles() {
|
||||
const l = document.createElement('script');
|
||||
l.src = 'common.js';
|
||||
l.onload = () => loadResources(['style.css'], S); // load style.css then call S()
|
||||
l.onerror = () => setTimeout(loadFiles, 100);
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
|
||||
var scanLoops = 0, preScanSSID = "";
|
||||
var maxNetworks = 3;
|
||||
function N() {
|
||||
@@ -178,9 +187,8 @@ Static subnet mask:<br>
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<body>
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="H('features/settings/#wifi-settings')">?</button></div>
|
||||
|
||||
@@ -157,12 +157,6 @@ void sendDataWs(AsyncWebSocketClient * client)
|
||||
// the following may no longer be necessary as heap management has been fixed by @willmmiles in AWS
|
||||
size_t heap1 = getFreeHeapSize();
|
||||
DEBUG_PRINTF_P(PSTR("heap %u\n"), getFreeHeapSize());
|
||||
#ifdef ESP8266
|
||||
if (len>heap1) {
|
||||
DEBUG_PRINTLN(F("Out of memory (WS)!"));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
AsyncWebSocketBuffer buffer(len);
|
||||
#ifdef ESP8266
|
||||
size_t heap2 = getFreeHeapSize();
|
||||
|
||||
Reference in New Issue
Block a user