mirror of
https://github.com/Laxilef/OTGateway.git
synced 2026-02-02 23:57:44 +05:00
feat: added multilanguage for portal
This commit is contained in:
1
src_data/scripts/i18n.min.js
vendored
Normal file
1
src_data/scripts/i18n.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
(function(){var e,t,n,r=function(e,t){return function(){return e.apply(t,arguments)}};e=function(){function e(){this.translate=r(this.translate,this);this.data={values:{},contexts:[]};this.globalContext={}}e.prototype.translate=function(e,t,n,r,i){var s,o,u,a;if(i==null){i=this.globalContext}u=function(e){var t;t=typeof e;return t==="function"||t==="object"&&!!e};if(u(t)){s=null;a=null;o=t;i=n||this.globalContext}else{if(typeof t==="number"){s=null;a=t;o=n;i=r||this.globalContext}else{s=t;if(typeof n==="number"){a=n;o=r;i=i}else{a=null;o=n;i=r||this.globalContext}}}if(u(e)){if(u(e["i18n"])){e=e["i18n"]}return this.translateHash(e,i)}else{return this.translateText(e,a,o,i,s)}};e.prototype.add=function(e){var t,n,r,i,s,o,u,a;if(e.values!=null){o=e.values;for(n in o){r=o[n];this.data.values[n]=r}}if(e.contexts!=null){u=e.contexts;a=[];for(i=0,s=u.length;i<s;i++){t=u[i];a.push(this.data.contexts.push(t))}return a}};e.prototype.setContext=function(e,t){return this.globalContext[e]=t};e.prototype.clearContext=function(e){return this.lobalContext[e]=null};e.prototype.reset=function(){this.data={values:{},contexts:[]};return this.globalContext={}};e.prototype.resetData=function(){return this.data={values:{},contexts:[]}};e.prototype.resetContext=function(){return this.globalContext={}};e.prototype.translateHash=function(e,t){var n,r;for(n in e){r=e[n];if(typeof r==="string"){e[n]=this.translateText(r,null,null,t)}}return e};e.prototype.translateText=function(e,t,n,r,i){var s,o;if(r==null){r=this.globalContext}if(this.data==null){return this.useOriginalText(i||e,t,n)}s=this.getContextData(this.data,r);if(s!=null){o=this.findTranslation(e,t,n,s.values,i)}if(o==null){o=this.findTranslation(e,t,n,this.data.values,i)}if(o==null){return this.useOriginalText(i||e,t,n)}return o};e.prototype.findTranslation=function(e,t,n,r){var i,s,o,u,a;o=r[e];if(o==null){return null}if(t==null){if(typeof o==="string"){return this.applyFormatting(o,t,n)}}else{if(o instanceof Array||o.length){for(u=0,a=o.length;u<a;u++){s=o[u];if((t>=s[0]||s[0]===null)&&(t<=s[1]||s[1]===null)){i=this.applyFormatting(s[2].replace("-%n",String(-t)),t,n);return this.applyFormatting(i.replace("%n",String(t)),t,n)}}}}return null};e.prototype.getContextData=function(e,t){var n,r,i,s,o,u,a,f;if(e.contexts==null){return null}a=e.contexts;for(o=0,u=a.length;o<u;o++){n=a[o];r=true;f=n.matches;for(i in f){s=f[i];r=r&&s===t[i]}if(r){return n}}return null};e.prototype.useOriginalText=function(e,t,n){if(t==null){return this.applyFormatting(e,t,n)}return this.applyFormatting(e.replace("%n",String(t)),t,n)};e.prototype.applyFormatting=function(e,t,n){var r,i;for(r in n){i=new RegExp("%{"+r+"}","g");e=e.replace(i,n[r])}return e};return e}();n=new e;t=n.translate;t.translator=n;t.create=function(n){var r;r=new e;if(n!=null){r.add(n)}r.translate.create=t.create;return r.translate};(typeof module!=="undefined"&&module!==null?module.exports=t:void 0)||(this.i18n=t)}).call(this)
|
||||
128
src_data/scripts/lang.js
Normal file
128
src_data/scripts/lang.js
Normal file
@@ -0,0 +1,128 @@
|
||||
class Lang {
|
||||
constructor(switcher, defaultLocale = null) {
|
||||
if (!(switcher instanceof Object)) {
|
||||
throw new SyntaxError("switcher must be an element object");
|
||||
}
|
||||
|
||||
this.switcher = switcher;
|
||||
this.defaultLocale = defaultLocale;
|
||||
this.supportedLocales = [];
|
||||
this.currentLocale = null;
|
||||
}
|
||||
|
||||
async build() {
|
||||
this.bindSwitcher();
|
||||
|
||||
const userLocale = localStorage.getItem('locale');
|
||||
if (this.localeIsSupported(userLocale)) {
|
||||
await this.setLocale(userLocale);
|
||||
|
||||
} else {
|
||||
const initialLocale = this.getSuitableLocale(this.browserLocales(true));
|
||||
await this.setLocale(initialLocale);
|
||||
}
|
||||
|
||||
this.translatePage();
|
||||
}
|
||||
|
||||
bindSwitcher() {
|
||||
this.supportedLocales = [];
|
||||
for (const option of this.switcher.options) {
|
||||
this.supportedLocales.push(option.value);
|
||||
}
|
||||
|
||||
if (!this.localeIsSupported(this.defaultLocale)) {
|
||||
const selected = this.switcher.selectedIndex ?? 0;
|
||||
this.defaultLocale = this.switcher.options[selected].value;
|
||||
}
|
||||
|
||||
this.switcher.addEventListener('change', async (element) => {
|
||||
await this.setLocale(element.target.value);
|
||||
this.translatePage();
|
||||
});
|
||||
}
|
||||
|
||||
async setLocale(newLocale) {
|
||||
if (this.currentLocale == newLocale) {
|
||||
return;
|
||||
}
|
||||
|
||||
i18n.translator.reset();
|
||||
i18n.translator.add(await this.fetchTranslations(newLocale));
|
||||
|
||||
this.currentLocale = newLocale;
|
||||
localStorage.setItem('locale', this.currentLocale);
|
||||
|
||||
if (document.documentElement) {
|
||||
document.documentElement.setAttribute("lang", this.currentLocale);
|
||||
}
|
||||
|
||||
if (this.switcher.value != this.currentLocale) {
|
||||
this.switcher.value = this.currentLocale;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchTranslations(locale) {
|
||||
const response = await fetch(`/static/locales/${locale}.json`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.values instanceof Object) {
|
||||
data.values = this.flattenKeys({keys: data.values, prefix: ''});
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
translatePage() {
|
||||
document
|
||||
.querySelectorAll("[data-i18n]")
|
||||
.forEach((element) => this.translateElement(element));
|
||||
}
|
||||
|
||||
translateElement(element) {
|
||||
let key = element.getAttribute("data-i18n");
|
||||
if (!key && element.innerHTML) {
|
||||
key = element.innerHTML;
|
||||
element.setAttribute("data-i18n", key);
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
|
||||
const arg = element.getAttribute("data-i18n-arg") || null;
|
||||
const options = JSON.parse(element.getAttribute("data-i18n-options")) || null;
|
||||
|
||||
element.innerHTML = i18n(key, arg, options);
|
||||
}
|
||||
|
||||
|
||||
localeIsSupported(locale) {
|
||||
return locale !== null && this.supportedLocales.indexOf(locale) > -1;
|
||||
}
|
||||
|
||||
getSuitableLocale(locales) {
|
||||
return locales.find(this.localeIsSupported) || this.defaultLocale;
|
||||
}
|
||||
|
||||
browserLocales(codeOnly = false) {
|
||||
return navigator.languages.map((locale) =>
|
||||
codeOnly ? locale.split("-")[0] : locale,
|
||||
);
|
||||
}
|
||||
|
||||
flattenKeys({ keys, prefix }) {
|
||||
let result = {};
|
||||
for (let key in keys) {
|
||||
const type = typeof keys[key];
|
||||
if (type === 'string') {
|
||||
result[`${prefix}${key}`] = keys[key];
|
||||
}
|
||||
else if (type === 'object') {
|
||||
result = { ...result, ...this.flattenKeys({ keys: keys[key], prefix: `${prefix}${key}.` }) }
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
680
src_data/scripts/utils.js
Normal file
680
src_data/scripts/utils.js
Normal file
@@ -0,0 +1,680 @@
|
||||
function setupForm(formSelector, onResultCallback = null, noCastItems = []) {
|
||||
const form = document.querySelector(formSelector);
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
|
||||
form.querySelectorAll('input').forEach(item => {
|
||||
item.addEventListener('change', (e) => {
|
||||
e.target.setAttribute('aria-invalid', !e.target.checkValidity());
|
||||
})
|
||||
});
|
||||
|
||||
const url = form.action;
|
||||
let button = form.querySelector('button[type="submit"]');
|
||||
let defaultText;
|
||||
|
||||
if (button) {
|
||||
defaultText = button.textContent;
|
||||
}
|
||||
|
||||
form.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (button) {
|
||||
button.textContent = i18n("button.wait");
|
||||
button.setAttribute('disabled', true);
|
||||
button.setAttribute('aria-busy', true);
|
||||
}
|
||||
|
||||
const onSuccess = (result) => {
|
||||
if (button) {
|
||||
button.textContent = i18n('button.saved');
|
||||
button.classList.add('success');
|
||||
button.removeAttribute('aria-busy');
|
||||
|
||||
setTimeout(() => {
|
||||
button.removeAttribute('disabled');
|
||||
button.classList.remove('success', 'failed');
|
||||
button.textContent = defaultText;
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
const onFailed = () => {
|
||||
if (button) {
|
||||
button.textContent = i18n('button.error');
|
||||
button.classList.add('failed');
|
||||
button.removeAttribute('aria-busy');
|
||||
|
||||
setTimeout(() => {
|
||||
button.removeAttribute('disabled');
|
||||
button.classList.remove('success', 'failed');
|
||||
button.textContent = defaultText;
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
let fd = new FormData(form);
|
||||
let checkboxes = form.querySelectorAll('input[type="checkbox"]');
|
||||
for (let checkbox of checkboxes) {
|
||||
fd.append(checkbox.getAttribute('name'), checkbox.checked);
|
||||
}
|
||||
|
||||
let response = await fetch(url, {
|
||||
method: 'POST',
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: form2json(fd, noCastItems)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Response not valid');
|
||||
}
|
||||
|
||||
const result = response.status != 204 ? (await response.json()) : null;
|
||||
onSuccess(result);
|
||||
|
||||
if (onResultCallback instanceof Function) {
|
||||
onResultCallback(result);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
onFailed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setupNetworkScanForm(formSelector, tableSelector) {
|
||||
const form = document.querySelector(formSelector);
|
||||
if (!form) {
|
||||
console.error("form not found");
|
||||
return;
|
||||
}
|
||||
|
||||
const url = form.action;
|
||||
let button = form.querySelector('button[type="submit"]');
|
||||
let defaultText;
|
||||
|
||||
if (button) {
|
||||
defaultText = button.innerHTML;
|
||||
}
|
||||
|
||||
const onSubmitFn = async (event) => {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
if (button) {
|
||||
button.innerHTML = i18n('button.wait');
|
||||
button.setAttribute('disabled', true);
|
||||
button.setAttribute('aria-busy', true);
|
||||
}
|
||||
|
||||
let table = document.querySelector(tableSelector);
|
||||
if (!table) {
|
||||
console.error("table not found");
|
||||
return;
|
||||
}
|
||||
|
||||
const onSuccess = async (response) => {
|
||||
let result = await response.json();
|
||||
console.log('networks: ', result);
|
||||
|
||||
let tbody = table.querySelector('tbody');
|
||||
if (!tbody) {
|
||||
tbody = table.createTBody();
|
||||
}
|
||||
|
||||
while (tbody.rows.length > 0) {
|
||||
tbody.rows[0].remove();
|
||||
}
|
||||
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
let row = tbody.insertRow(-1);
|
||||
row.classList.add("network");
|
||||
row.setAttribute('data-ssid', result[i].hidden ? '' : result[i].ssid);
|
||||
row.onclick = function () {
|
||||
const input = document.querySelector('input#sta-ssid');
|
||||
const ssid = this.getAttribute('data-ssid');
|
||||
if (!input || !ssid) {
|
||||
return;
|
||||
}
|
||||
|
||||
input.value = ssid;
|
||||
input.focus();
|
||||
};
|
||||
|
||||
row.insertCell().textContent = "#" + (i + 1);
|
||||
row.insertCell().innerHTML = result[i].hidden ? ("<i>" + result[i].bssid + "</i>") : result[i].ssid;
|
||||
|
||||
// info cell
|
||||
let infoCell = row.insertCell();
|
||||
|
||||
// signal quality
|
||||
let signalQualityIcon = document.createElement("i");
|
||||
if (result[i].signalQuality > 80) {
|
||||
signalQualityIcon.classList.add('icons-wifi-strength-4');
|
||||
} else if (result[i].signalQuality > 60) {
|
||||
signalQualityIcon.classList.add('icons-wifi-strength-3');
|
||||
} else if (result[i].signalQuality > 40) {
|
||||
signalQualityIcon.classList.add('icons-wifi-strength-2');
|
||||
} else if (result[i].signalQuality > 20) {
|
||||
signalQualityIcon.classList.add('icons-wifi-strength-1');
|
||||
} else {
|
||||
signalQualityIcon.classList.add('icons-wifi-strength-0');
|
||||
}
|
||||
|
||||
let signalQualityContainer = document.createElement("span");
|
||||
signalQualityContainer.setAttribute('data-tooltip', result[i].signalQuality + "%");
|
||||
signalQualityContainer.appendChild(signalQualityIcon);
|
||||
infoCell.appendChild(signalQualityContainer);
|
||||
|
||||
// auth
|
||||
const authList = {
|
||||
0: "Open",
|
||||
1: "WEP",
|
||||
2: "WPA",
|
||||
3: "WPA2",
|
||||
4: "WPA/WPA2",
|
||||
5: "WPA/WPA2 Enterprise",
|
||||
6: "WPA3",
|
||||
7: "WPA2/WPA3",
|
||||
8: "WAPI",
|
||||
9: "OWE",
|
||||
10: "WPA3 Enterprise"
|
||||
};
|
||||
let authIcon = document.createElement("i");
|
||||
|
||||
if (result[i].auth == 0) {
|
||||
authIcon.classList.add('icons-unlocked');
|
||||
} else {
|
||||
authIcon.classList.add('icons-locked');
|
||||
}
|
||||
|
||||
let authContainer = document.createElement("span");
|
||||
authContainer.setAttribute('data-tooltip', (result[i].auth in authList) ? authList[result[i].auth] : "unknown");
|
||||
authContainer.appendChild(authIcon);
|
||||
infoCell.appendChild(authContainer);
|
||||
}
|
||||
|
||||
if (button) {
|
||||
button.innerHTML = defaultText;
|
||||
button.removeAttribute('disabled');
|
||||
button.removeAttribute('aria-busy');
|
||||
}
|
||||
};
|
||||
|
||||
const onFailed = async (response) => {
|
||||
table.classList.remove('hidden');
|
||||
|
||||
if (button) {
|
||||
button.innerHTML = defaultText;
|
||||
button.removeAttribute('disabled');
|
||||
button.removeAttribute('aria-busy');
|
||||
}
|
||||
};
|
||||
|
||||
let attempts = 6;
|
||||
let attemptFn = async () => {
|
||||
attempts--;
|
||||
|
||||
try {
|
||||
let response = await fetch(url, { cache: 'no-cache' });
|
||||
|
||||
if (response.status == 200) {
|
||||
await onSuccess(response);
|
||||
|
||||
} else if (attempts <= 0) {
|
||||
await onFailed(response);
|
||||
|
||||
} else {
|
||||
setTimeout(attemptFn, 5000);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
if (attempts <= 0) {
|
||||
onFailed(err);
|
||||
|
||||
} else {
|
||||
setTimeout(attemptFn, 10000);
|
||||
}
|
||||
}
|
||||
};
|
||||
attemptFn();
|
||||
};
|
||||
|
||||
form.addEventListener('submit', onSubmitFn);
|
||||
onSubmitFn();
|
||||
}
|
||||
|
||||
function setupRestoreBackupForm(formSelector) {
|
||||
const form = document.querySelector(formSelector);
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = form.action;
|
||||
let button = form.querySelector('button[type="submit"]');
|
||||
let defaultText;
|
||||
|
||||
if (button) {
|
||||
defaultText = button.textContent;
|
||||
}
|
||||
|
||||
form.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (button) {
|
||||
button.textContent = i18n('button.wait');
|
||||
button.setAttribute('disabled', true);
|
||||
button.setAttribute('aria-busy', true);
|
||||
}
|
||||
|
||||
const onSuccess = (response) => {
|
||||
if (button) {
|
||||
button.textContent = i18n('button.restored');
|
||||
button.classList.add('success');
|
||||
button.removeAttribute('aria-busy');
|
||||
|
||||
setTimeout(() => {
|
||||
button.removeAttribute('disabled');
|
||||
button.classList.remove('success', 'failed');
|
||||
button.textContent = defaultText;
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
const onFailed = (response) => {
|
||||
if (button) {
|
||||
button.textContent = i18n('button.error');
|
||||
button.classList.add('failed');
|
||||
button.removeAttribute('aria-busy');
|
||||
|
||||
setTimeout(() => {
|
||||
button.removeAttribute('disabled');
|
||||
button.classList.remove('success', 'failed');
|
||||
button.textContent = defaultText;
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
const files = form.querySelector('#restore-file').files;
|
||||
if (files.length <= 0) {
|
||||
onFailed(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let reader = new FileReader();
|
||||
reader.readAsText(files[0]);
|
||||
reader.onload = async function () {
|
||||
try {
|
||||
let response = await fetch(url, {
|
||||
method: 'POST',
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: reader.result
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
onSuccess(response);
|
||||
|
||||
} else {
|
||||
onFailed(response);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
onFailed(false);
|
||||
}
|
||||
};
|
||||
reader.onerror = function () {
|
||||
console.log(reader.error);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function setupUpgradeForm(formSelector) {
|
||||
const form = document.querySelector(formSelector);
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = form.action;
|
||||
let button = form.querySelector('button[type="submit"]');
|
||||
let defaultText;
|
||||
|
||||
if (button) {
|
||||
defaultText = button.textContent;
|
||||
}
|
||||
|
||||
const statusToText = (status) => {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return "None";
|
||||
case 1:
|
||||
return "No file";
|
||||
case 2:
|
||||
return "Success";
|
||||
case 3:
|
||||
return "Prohibited";
|
||||
case 4:
|
||||
return "Aborted";
|
||||
case 5:
|
||||
return "Error on start";
|
||||
case 6:
|
||||
return "Error on write";
|
||||
case 7:
|
||||
return "Error on finish";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
};
|
||||
|
||||
const onResult = async (response) => {
|
||||
if (!response) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
let resItem = form.querySelector('.upgrade-firmware-result');
|
||||
if (resItem && result.firmware.status > 1) {
|
||||
resItem.textContent = statusToText(result.firmware.status);
|
||||
resItem.classList.remove('hidden');
|
||||
|
||||
if (result.firmware.status == 2) {
|
||||
resItem.classList.remove('failed');
|
||||
resItem.classList.add('success');
|
||||
} else {
|
||||
resItem.classList.remove('success');
|
||||
resItem.classList.add('failed');
|
||||
|
||||
if (result.firmware.error != "") {
|
||||
resItem.textContent += ": " + result.firmware.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resItem = form.querySelector('.upgrade-filesystem-result');
|
||||
if (resItem && result.filesystem.status > 1) {
|
||||
resItem.textContent = statusToText(result.filesystem.status);
|
||||
resItem.classList.remove('hidden');
|
||||
|
||||
if (result.filesystem.status == 2) {
|
||||
resItem.classList.remove('failed');
|
||||
resItem.classList.add('success');
|
||||
} else {
|
||||
resItem.classList.remove('success');
|
||||
resItem.classList.add('failed');
|
||||
|
||||
if (result.filesystem.error != "") {
|
||||
resItem.textContent += ": " + result.filesystem.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onSuccess = (response) => {
|
||||
onResult(response);
|
||||
|
||||
if (button) {
|
||||
button.textContent = defaultText;
|
||||
button.removeAttribute('aria-busy');
|
||||
|
||||
setTimeout(() => {
|
||||
button.removeAttribute('disabled');
|
||||
button.classList.remove('success', 'failed');
|
||||
button.textContent = defaultText;
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
const onFailed = (response) => {
|
||||
if (button) {
|
||||
button.textContent = i18n('button.error');
|
||||
button.classList.add('failed');
|
||||
button.removeAttribute('aria-busy');
|
||||
|
||||
setTimeout(() => {
|
||||
button.removeAttribute('disabled');
|
||||
button.classList.remove('success', 'failed');
|
||||
button.textContent = defaultText;
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
form.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
hide('.upgrade-firmware-result');
|
||||
hide('.upgrade-filesystem-result');
|
||||
|
||||
if (button) {
|
||||
button.textContent = i18n('button.uploading');
|
||||
button.setAttribute('disabled', true);
|
||||
button.setAttribute('aria-busy', true);
|
||||
}
|
||||
|
||||
try {
|
||||
let fd = new FormData(form);
|
||||
let response = await fetch(url, {
|
||||
method: 'POST',
|
||||
cache: 'no-cache',
|
||||
body: fd
|
||||
});
|
||||
|
||||
if (response.status >= 200 && response.status < 500) {
|
||||
onSuccess(response);
|
||||
|
||||
} else {
|
||||
onFailed(response);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
onFailed(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function setBusy(busySelector, contentSelector, value) {
|
||||
if (!value) {
|
||||
hide(busySelector);
|
||||
show(contentSelector);
|
||||
|
||||
} else {
|
||||
show(busySelector);
|
||||
hide(contentSelector);
|
||||
}
|
||||
}
|
||||
|
||||
function setState(selector, value) {
|
||||
let item = document.querySelector(selector);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.setAttribute('aria-invalid', !value);
|
||||
}
|
||||
|
||||
function setValue(selector, value) {
|
||||
let items = document.querySelectorAll(selector);
|
||||
if (!items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let item of items) {
|
||||
item.innerHTML = value;
|
||||
}
|
||||
}
|
||||
|
||||
function setCheckboxValue(selector, value) {
|
||||
let item = document.querySelector(selector);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.checked = value;
|
||||
}
|
||||
|
||||
function setRadioValue(selector, value) {
|
||||
let items = document.querySelectorAll(selector);
|
||||
if (!items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let item of items) {
|
||||
item.checked = item.value == value;
|
||||
}
|
||||
}
|
||||
|
||||
function setInputValue(selector, value, attrs = {}) {
|
||||
let items = document.querySelectorAll(selector);
|
||||
if (!items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let item of items) {
|
||||
item.value = value;
|
||||
|
||||
if (attrs instanceof Object) {
|
||||
for (let attrKey of Object.keys(attrs)) {
|
||||
item.setAttribute(attrKey, attrs[attrKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function show(selector) {
|
||||
let items = document.querySelectorAll(selector);
|
||||
if (!items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let item of items) {
|
||||
if (item.classList.contains('hidden')) {
|
||||
item.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hide(selector) {
|
||||
let items = document.querySelectorAll(selector);
|
||||
if (!items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let item of items) {
|
||||
if (!item.classList.contains('hidden')) {
|
||||
item.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unit2str(unitSystem, units = {}, defaultValue = '?') {
|
||||
return (unitSystem in units)
|
||||
? units[unitSystem]
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
function temperatureUnit(unitSystem) {
|
||||
return unit2str(unitSystem, {
|
||||
0: "°C",
|
||||
1: "°F"
|
||||
});
|
||||
}
|
||||
|
||||
function pressureUnit(unitSystem) {
|
||||
return unit2str(unitSystem, {
|
||||
0: "bar",
|
||||
1: "psi"
|
||||
});
|
||||
}
|
||||
|
||||
function volumeUnit(unitSystem) {
|
||||
return unit2str(unitSystem, {
|
||||
0: "L",
|
||||
1: "gal"
|
||||
});
|
||||
}
|
||||
|
||||
function memberIdToVendor(memberId) {
|
||||
// https://github.com/Jeroen88/EasyOpenTherm/blob/main/src/EasyOpenTherm.h
|
||||
// https://github.com/Evgen2/SmartTherm/blob/v0.7/src/Web.cpp
|
||||
const vendorList = {
|
||||
1: "Baxi Fourtech/Luna 3",
|
||||
2: "AWB/Brink",
|
||||
4: "ATAG/Brötje/ELCO/GEMINOX",
|
||||
5: "Itho Daalderop",
|
||||
6: "IDEAL",
|
||||
8: "Buderus/Bosch/Hoval",
|
||||
9: "Ferroli",
|
||||
11: "Remeha/De Dietrich",
|
||||
16: "Unical",
|
||||
24: "Vaillant/Bulex",
|
||||
27: "Baxi",
|
||||
29: "Itho Daalderop",
|
||||
33: "Viessmann",
|
||||
41: "Italtherm",
|
||||
56: "Baxi Luna Duo-Tec",
|
||||
131: "Nefit",
|
||||
148: "Navien",
|
||||
173: "Intergas",
|
||||
247: "Baxi Ampera",
|
||||
248: "Zota Lux-X"
|
||||
};
|
||||
|
||||
return (memberId in vendorList)
|
||||
? vendorList[memberId]
|
||||
: "unknown vendor";
|
||||
}
|
||||
|
||||
function form2json(data, noCastItems = []) {
|
||||
let method = function (object, pair) {
|
||||
let keys = pair[0].replace(/\]/g, '').split('[');
|
||||
let key = keys[0];
|
||||
let value = pair[1];
|
||||
|
||||
if (!noCastItems.includes(keys.join('.'))) {
|
||||
if (value === 'true' || value === 'false') {
|
||||
value = value === 'true';
|
||||
|
||||
} else if (typeof (value) === 'string' && value.trim() !== '' && !isNaN(value)) {
|
||||
value = parseFloat(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (keys.length > 1) {
|
||||
let i, x, segment;
|
||||
let last = value;
|
||||
let type = isNaN(keys[1]) ? {} : [];
|
||||
value = segment = object[key] || type;
|
||||
|
||||
for (i = 1; i < keys.length; i++) {
|
||||
x = keys[i];
|
||||
if (i == keys.length - 1) {
|
||||
if (Array.isArray(segment)) {
|
||||
segment.push(last);
|
||||
} else {
|
||||
segment[x] = last;
|
||||
}
|
||||
} else if (segment[x] == undefined) {
|
||||
segment[x] = isNaN(keys[i + 1]) ? {} : [];
|
||||
}
|
||||
segment = segment[x];
|
||||
}
|
||||
}
|
||||
|
||||
object[key] = value;
|
||||
return object;
|
||||
}
|
||||
|
||||
let object = Array.from(data).reduce(method, {});
|
||||
return JSON.stringify(object);
|
||||
}
|
||||
Reference in New Issue
Block a user