buttonClose.addEventListener('click', hide);
buttonYes.addEventListener('click', hide);
buttonNo.addEventListener('click', hide);
return {
alert,
confirm,
process,
}
}
_initLoginForm() {
const element = document.getElementById('login-form');
const login = document.getElementById('login');
const password = document.getElementById('password');
const buttonYes = element.querySelector('.popup-yes');
const submit = async () => {
element.classList.add('hidden');
const result = await _postData({cmd: 'login', user: login.value, password: password.value});
if (!result.status) {
window.location.reload();
}
};
const show = () => {
this.disableUI();
login.value = '';
password.value = '';
element.classList.remove('hidden');
};
login.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
submit()
}
});
password.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
submit()
}
});
buttonYes.addEventListener('click', submit);
return {
show,
async logout() {
await _postData({cmd: 'logout'});
window.location.reload();
},
}
}
setStatus(status) {
document.body.classList.toggle('running', status);
}
setChanged(status) {
document.body.classList.toggle('changed', status);
}
isChanged() {
return document.body.classList.contains('changed');
}
_initButtons() {
const btnReload = document.getElementById('reload');
const btnRestart = document.getElementById('restart');
const btnStop = document.getElementById('stop');
const btnStart = document.getElementById('start');
const btnDropdown = document.getElementById('dropdown');
const menuDropdown = document.getElementById('dropdown-menu');
const btnSave = document.getElementById('save');
const btnTheme = document.getElementById('theme');
const btnUpgrade = document.getElementById('upgrade');
const btnLogout = document.getElementById('logout');
const nfqwsActionClick = async (action, text) => {
const yesno = await this.popup.confirm(text);
if (!yesno) {
return;
}
const result = await this.popup.process(`nfqws-keenetic ${action}`, serviceAction, action);
if (result) {
if (action === 'stop') {
this.setStatus(false);
} else if (action === 'start' || action === 'restart') {
this.setStatus(true);
}
}
return result;
};
btnReload.addEventListener('click', () => nfqwsActionClick('reload', 'Reload service?'));
btnRestart.addEventListener('click', () => nfqwsActionClick('restart', 'Restart service?'));
btnStop.addEventListener('click', () => nfqwsActionClick('stop', 'Stop service?'));
btnStart.addEventListener('click', () => nfqwsActionClick('start', 'Start service?'));
btnTheme.addEventListener('click', () => this.toggleTheme());
btnUpgrade.addEventListener('click', async () => {
const result = await nfqwsActionClick('upgrade', 'Update nfqws-keenetic?');
if (result) {
window.location.reload();
}
});
btnLogout.addEventListener('click', () => this.login.logout());
btnDropdown.addEventListener('click', () => {
menuDropdown.classList.toggle('hidden');
});
const hideMenu = _debounce(() => {
menuDropdown.classList.add('hidden');
}, 500);
btnDropdown.addEventListener('focusout', hideMenu);
menuDropdown.addEventListener('mouseleave', hideMenu);
menuDropdown.addEventListener('mouseenter', () => hideMenu.stop());
btnSave.addEventListener('click', async () => {
if (!this.isChanged()) {
return;
}
const result = await saveFile(this.tabs.currentFileName, this.textarea.value);
if (!result.status) {
this.textarea.save();
} else {
this.popup.alert(`save ${this.tabs.currentFileName}`, `Error: ${result.status}`);
}
});
return {
click() {
btnSave.click();
},
};
}
async loadFile(filename) {
if (this.textarea.changed) {
const yesno = await this.popup.confirm('File is not saved, close?');
if (!yesno) {
return;
}
}
this.tabs.activate(filename);
this.textarea.value = await getFileContent(filename);
this.textarea.readonly(filename.endsWith('.log'));
}
disableUI() {
this.textarea.disabled(true);
document.body.classList.add('disabled');
}
enableUI() {
this.textarea.disabled(false);
document.body.classList.remove('disabled', 'unknown');
}
toggleTheme() {
const root = document.querySelector(':root');
const theme = (root.dataset.theme === 'dark') ? 'light' : 'dark';
localStorage.setItem('theme', theme);
root.dataset.theme = theme;
}
}
function _debounce(func, ms) {
let timeout;
function wrapper(..._args) {
const _this = this;
if (timeout) {
window.clearTimeout(timeout);
}
timeout = window.setTimeout(() => {
func.apply(_this, _args);
}, ms);
}
wrapper.stop = () => {
if (timeout) {
window.clearTimeout(timeout);
}
};
return wrapper;
}
async function _postData(data) {
const formData = new FormData();
for (const [key, value] of Object.entries(data)) {
formData.append(key, value);
}
try {
const response = await fetch('index.php', {
method: 'POST',
body: formData,
});
if (response.ok) {
return await response.json();
}
if (response.status === 401) {
ui?.login.show();
}
return {status: response.status, statusText: response.statusText};
} catch (e) {
return {status: 975};
}
}
async function getFiles() {
return _postData({cmd: 'filenames'});
}
async function getFileContent(filename) {
const data = await _postData({cmd: 'filecontent', filename});
return data.content || '';
}
async function saveFile(filename, content) {
return _postData({cmd: 'filesave', filename, content});
}
async function removeFile(filename) {
return _postData({cmd: 'fileremove', filename});
}
async function serviceAction(action) {
return _postData({cmd: action});
}
async function getLatestVersion() {
try {
const response = await fetch('https://api.github.com/repos/Anonym-tsk/nfqws-keenetic/releases/latest');
const data = await response.json();
const tag = data.tag_name;
const match = tag.match(/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/);
return [match[1], match[2], match[3]];
} catch (e) {
return null;
}
}
function compareVersions(current, latest) {
const v1 = latest[0] - current[0];
const v2 = latest[1] - current[1];
const v3 = latest[2] - current[2];
if (v1) return v1 > 0;
if (v2) return v2 > 0;
if (v3) return v3 > 0;
return false;
}
const ui = new UI();
ui.version.checkUpdate();
const response = await getFiles();
ui.setStatus(response.service);
if (response.files?.length) {
for (const filename of response.files) {
ui.tabs.add(filename);
}
ui.tabs.activateFirst();
ui.enableUI();
}