+
-
+
${tools.escape(name)}
@@ -126,10 +142,16 @@ function __makeApp(id, path, icon, name) {
function __logout() {
tools.httpPost("api/auth/logout", null, function(http) {
- if (http.status === 200 || http.status === 401 || http.status === 403) {
- tools.currentOpen("login");
- } else {
- wm.error("Logout error", http.responseText);
+ switch (http.status) {
+ case 200:
+ case 401:
+ case 403:
+ tools.currentOpen("login");
+ break;
+
+ default:
+ wm.error("Logout error", http.responseText);
+ break;
}
});
}
diff --git a/web/share/js/ipmi/main.js b/web/share/js/ipmi/main.js
index 3f08e315..570cb839 100644
--- a/web/share/js/ipmi/main.js
+++ b/web/share/js/ipmi/main.js
@@ -32,29 +32,43 @@ export function main() {
function __loadKvmdInfo() {
tools.httpGet("api/info", null, function(http) {
- if (http.status === 200) {
- let ipmi_port = JSON.parse(http.responseText).result.extras.ipmi.port;
- let make_item = (comment, ipmi, api) => `
-
- ipmitool -I lanplus -U admin -P admin -H ${window.location.hostname} -p ${ipmi_port} ${ipmi}
- curl -XPOST -HX-KVMD-User:admin -HX-KVMD-Passwd:admin -k \\
- ${window.location.protocol}//${window.location.host}/api/atx${api}
- `;
- $("ipmi-text").innerHTML = `
- ${make_item("Power on the server if it's off", "power on", "/power?action=on")}
-
- ${make_item("Soft power off the server if it's on", "power soft", "/power?action=off")}
-
- ${make_item("Hard power off the server if it's on", "power off", "/power?action=off_hard")}
-
- ${make_item("Hard reset the server if it's on", "power reset", "/power?action=reset_hard")}
-
- ${make_item("Check the power status", "power status", "")}
- `;
- } else if (http.status === 401 || http.status === 403) {
- tools.currentOpen("login");
- } else {
- setTimeout(__loadKvmdInfo, 1000);
+ switch (http.status) {
+ case 200:
+ __showKvmdInfo(JSON.parse(http.responseText).result);
+ break;
+
+ case 401:
+ case 403:
+ tools.currentOpen("login");
+ break;
+
+ default:
+ setTimeout(__loadKvmdInfo, 1000);
+ break;
}
});
}
+
+function __showKvmdInfo(info) {
+ let make_item = function (comment, cmd, api) {
+ return `
+
+ ipmitool -I lanplus -U admin -P admin
+ -H ${tools.escape(window.location.hostname)}
+ -p ${tools.escape(info.extras.ipmi.port)} ${tools.escape(cmd)}
+
+
+ curl -XPOST -HX-KVMD-User:admin -HX-KVMD-Passwd:admin -k \\
+ ${tools.escape(window.location.protocol + "//" + window.location.host + "/api/atx" + api)}
+ `;
+ };
+ $("ipmi-text").innerHTML = [
+ make_item("Power on the server if it's off", "power on", "/power?action=on"),
+ make_item("Soft power off the server if it's on", "power soft", "/power?action=off"),
+ make_item("Hard power off the server if it's on", "power off", "/power?action=off_hard"),
+ make_item("Hard reset the server if it's on", "power reset", "/power?action=reset_hard"),
+ make_item("Check the power status", "power status", ""),
+ ].join("
");
+}
diff --git a/web/share/js/kvm/atx.js b/web/share/js/kvm/atx.js
index 6bdcf571..69a0d2c5 100644
--- a/web/share/js/kvm/atx.js
+++ b/web/share/js/kvm/atx.js
@@ -106,7 +106,7 @@ export function Atx(__recorder) {
if ($("atx-ask-switch").checked) {
wm.confirm(`
- Are you sure you want to press the
${button} button?
+ Are you sure you want to press the
${tools.escape(button)} button?
Warning! This could cause data loss on the server.
`).then(function(ok) {
if (ok) {
diff --git a/web/share/js/kvm/gpio.js b/web/share/js/kvm/gpio.js
index d4221b3d..fd602ce8 100644
--- a/web/share/js/kvm/gpio.js
+++ b/web/share/js/kvm/gpio.js
@@ -135,30 +135,36 @@ export function Gpio(__recorder) {
var __createItem = function(item) {
if (item.type === "label") {
return item.text;
+
} else if (item.type === "input") {
+ let e_ch_class = tools.escape(`__gpio-led-${item.channel}`);
+ let e_icon = tools.escape(`${ROOT_PREFIX}share/svg/led-circle.svg`);
return `
`;
+
} else if (item.type === "output") {
let controls = [];
- let confirm = (item.confirm ? "Are you sure you want to perform this action?" : "");
+ let e_ch = tools.escape(item.channel);
+ let e_confirm = (item.confirm ? tools.escape("Are you sure you want to perform this action?") : "");
if (item.scheme["switch"]) {
- let id = tools.makeId();
+ let e_id = tools.escape(`__gpio-switch-${tools.makeRandomId()}`);
+ let e_ch_class = tools.escape(`__gpio-switch-${item.channel}`);
controls.push(`
-
+
@@ -166,22 +172,23 @@ export function Gpio(__recorder) {
`);
}
if (item.scheme.pulse.delay) {
+ let e_ch_class = tools.escape(`__gpio-button-${item.channel}`);
controls.push(`
- ${(item.hide ? "• " : "") + item.text}
+ ${(item.hide ? "• " : "") + tools.escape(item.text)}
`);
}
return ``;
- } else {
- return "";
}
+
+ return "";
};
var __setLedState = function(el, on) {
diff --git a/web/share/js/kvm/mouse.js b/web/share/js/kvm/mouse.js
index 3310d17e..4acf99a3 100644
--- a/web/share/js/kvm/mouse.js
+++ b/web/share/js/kvm/mouse.js
@@ -112,7 +112,7 @@ export function Mouse(__getGeometry, __recordWsEvent) {
};
var __updateRate = function(value) {
- $("hid-mouse-rate-value").innerHTML = value + " ms";
+ $("hid-mouse-rate-value").innerText = value + " ms";
tools.storage.set("hid.mouse.rate", value);
if (__timer) {
clearInterval(__timer);
@@ -121,13 +121,13 @@ export function Mouse(__getGeometry, __recordWsEvent) {
};
var __updateScrollRate = function(value) {
- $("hid-mouse-scroll-value").innerHTML = value;
+ $("hid-mouse-scroll-value").innerText = value;
tools.storage.set("hid.mouse.scroll_rate", value);
__scroll_rate = value;
};
var __updateRelativeSens = function(value) {
- $("hid-mouse-sens-value").innerHTML = value.toFixed(1);
+ $("hid-mouse-sens-value").innerText = value.toFixed(1);
tools.storage.set("hid.mouse.sens", value);
__relative_sens = value;
};
diff --git a/web/share/js/kvm/msd.js b/web/share/js/kvm/msd.js
index 13d53079..b513c08d 100644
--- a/web/share/js/kvm/msd.js
+++ b/web/share/js/kvm/msd.js
@@ -208,7 +208,7 @@ export function Msd() {
if (el.__names_json !== names_json) {
el.innerHTML = names.map(name => `
-
@@ -223,7 +223,7 @@ export function Msd() {
? `${names.length === 1 ? "Storage: %s" : "Internal storage: %s"}` // eslint-disable-line
: `Storage [${name}${part.writable ? "]" : ", read-only]"}: %s` // eslint-disable-line
);
- let id = `__msd-storage-${tools.makeIdByText(name)}-progress`;
+ let id = `__msd-storage-${tools.makeTextId(name)}-progress`;
tools.progress.setSizeOf($(id), title, part.size, part.free);
}
};
@@ -270,8 +270,8 @@ export function Msd() {
};
var __clickDownloadButton = function() {
- let image = encodeURIComponent($("msd-image-selector").value);
- tools.windowOpen(`api/msd/read?image=${image}`);
+ let e_image = encodeURIComponent($("msd-image-selector").value);
+ tools.windowOpen(`api/msd/read?image=${e_image}`);
};
var __clickRemoveButton = function() {
@@ -299,13 +299,13 @@ export function Msd() {
var __clickUploadNewButton = function() {
let file = tools.input.getFile($("msd-new-file"));
__http = new XMLHttpRequest();
- let prefix = encodeURIComponent($("msd-new-part-selector").value);
+ let e_prefix = encodeURIComponent($("msd-new-part-selector").value);
if (file) {
- let image = encodeURIComponent(file.name);
- __http.open("POST", `${ROOT_PREFIX}api/msd/write?prefix=${prefix}&image=${image}&remove_incomplete=1`, true);
+ let e_image = encodeURIComponent(file.name);
+ __http.open("POST", `${ROOT_PREFIX}api/msd/write?prefix=${e_prefix}&image=${e_image}&remove_incomplete=1`, true);
} else {
- let url = encodeURIComponent($("msd-new-url").value);
- __http.open("POST", `${ROOT_PREFIX}api/msd/write_remote?prefix=${prefix}&url=${url}&remove_incomplete=1`, true);
+ let e_url = encodeURIComponent($("msd-new-url").value);
+ __http.open("POST", `${ROOT_PREFIX}api/msd/write_remote?prefix=${e_prefix}&url=${e_url}&remove_incomplete=1`, true);
}
__http.upload.timeout = 7 * 24 * 3600;
__http.onreadystatechange = __uploadStateChange;
@@ -402,7 +402,8 @@ export function Msd() {
if (__state && __state.storage && __state.storage.parts) {
let part = __state.storage.parts[$("msd-new-part-selector").value];
if (part && (file.size > part.size)) {
- wm.error(`The new image is too big for the Mass Storage partition.
Maximum: ${tools.formatSize(part.size)}`);
+ let e_size = tools.escape(tools.formatSize(part.size));
+ wm.error(`The new image is too big for the Mass Storage partition.
Maximum: ${e_size}`);
el.value = "";
}
}
diff --git a/web/share/js/kvm/stream_media.js b/web/share/js/kvm/stream_media.js
index 0e552677..a0ea323a 100644
--- a/web/share/js/kvm/stream_media.js
+++ b/web/share/js/kvm/stream_media.js
@@ -220,7 +220,8 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
let width = frame.displayWidth;
let height = frame.displayHeight;
switch (__orient) {
- case 90: case 270:
+ case 90:
+ case 270:
width = frame.displayHeight;
height = frame.displayWidth;
}
diff --git a/web/share/js/kvm/stream_mjpeg.js b/web/share/js/kvm/stream_mjpeg.js
index a13e708e..46f3827b 100644
--- a/web/share/js/kvm/stream_mjpeg.js
+++ b/web/share/js/kvm/stream_mjpeg.js
@@ -32,7 +32,7 @@ export function MjpegStreamer(__setActive, __setInactive, __setInfo) {
/************************************************************************/
- var __key = tools.makeId();
+ var __key = tools.makeRandomId();
var __id = "";
var __fps = -1;
var __state = null;
@@ -91,7 +91,7 @@ export function MjpegStreamer(__setActive, __setInactive, __setInfo) {
var __setStreamInactive = function() {
let old_fps = __fps;
- __key = tools.makeId();
+ __key = tools.makeRandomId();
__id = "";
__fps = -1;
__state = null;
@@ -139,7 +139,7 @@ export function MjpegStreamer(__setActive, __setInactive, __setInfo) {
__setStreamInactive();
__stopChecking();
- let path = `${ROOT_PREFIX}streamer/stream?key=${__key}`;
+ let path = `${ROOT_PREFIX}streamer/stream?key=${encodeURIComponent(__key)}`;
if (tools.browser.is_safari || tools.browser.is_ios) {
// uStreamer fix for WebKit
__logInfo("Using dual_final_frames=1 to fix WebKit bugs");
diff --git a/web/share/js/kvm/switch.js b/web/share/js/kvm/switch.js
index 3fab203f..e54307f2 100644
--- a/web/share/js/kvm/switch.js
+++ b/web/share/js/kvm/switch.js
@@ -178,8 +178,8 @@ export function Switch() {
};
var __clickAddEdidButton = function() {
- let create_content = function(el_parent, el_ok_button) {
- tools.el.setEnabled(el_ok_button, false);
+ let create_content = function(el_parent, el_ok_bt) {
+ tools.el.setEnabled(el_ok_bt, false);
el_parent.innerHTML = `
@@ -203,7 +203,7 @@ export function Switch() {
el_name.oninput = el_data.oninput = function() {
let name = el_name.value.replace(/\s+/g, "");
let data = el_data.value.replace(/\s+/g, "");
- tools.el.setEnabled(el_ok_button, ((name.length > 0) && /[0-9a-fA-F]{512}/.test(data)));
+ tools.el.setEnabled(el_ok_bt, ((name.length > 0) && /[0-9a-fA-F]{512}/.test(data)));
};
};
@@ -584,7 +584,7 @@ export function Switch() {
};
if ($("switch-atx-ask-switch").checked) {
wm.confirm(`
- Are you sure you want to press the ${button} button?
+ Are you sure you want to press the ${tools.escape(button)} button?
Warning! This could cause data loss on the server.
`).then(function(ok) {
if (ok) {
diff --git a/web/share/js/login/main.js b/web/share/js/login/main.js
index 63fa0dec..fb962e7e 100644
--- a/web/share/js/login/main.js
+++ b/web/share/js/login/main.js
@@ -52,20 +52,28 @@ function __login() {
let passwd = $("passwd-input").value + $("code-input").value;
let body = `user=${encodeURIComponent(user)}&passwd=${encodeURIComponent(passwd)}`;
tools.httpPost("api/auth/login", null, function(http) {
- if (http.status === 200) {
- tools.currentOpen("");
- } else if (http.status === 403) {
- wm.error("Invalid credentials").then(__tryAgain);
- } else {
- let error = "";
- if (http.status === 400) {
- try { error = JSON.parse(http.responseText)["result"]["error"]; } catch { /* Nah */ }
- }
- if (error === "ValidatorError") {
- wm.error("Invalid characters in credentials").then(__tryAgain);
- } else {
- wm.error("Login error", http.responseText).then(__tryAgain);
- }
+ switch (http.status) {
+ case 200:
+ tools.currentOpen("");
+ break;
+
+ case 403:
+ wm.error("Invalid credentials").then(__tryAgain);
+ break;
+
+ default: {
+ let error = "";
+ if (http.status === 400) {
+ try {
+ error = JSON.parse(http.responseText)["result"]["error"];
+ } catch { /* Nah */ }
+ }
+ if (error === "ValidatorError") {
+ wm.error("Invalid characters in credentials").then(__tryAgain);
+ } else {
+ wm.error("Login error", http.responseText).then(__tryAgain);
+ }
+ } break;
}
}, body, "application/x-www-form-urlencoded");
__setEnabled(false);
diff --git a/web/share/js/tools.js b/web/share/js/tools.js
index 11f08735..4965a693 100644
--- a/web/share/js/tools.js
+++ b/web/share/js/tools.js
@@ -86,8 +86,11 @@ export var tools = new function() {
/************************************************************************/
self.escape = function(text) {
+ if (typeof text !== "string") {
+ text = "" + text;
+ }
return text.replace(
- /[^0-9A-Za-z ]/g,
+ /[^-_0-9A-Za-z ]/g,
ch => "" + ch.charCodeAt(0) + ";"
);
};
@@ -100,7 +103,7 @@ export var tools = new function() {
return text[0].toUpperCase() + text.slice(1);
};
- self.makeId = function() {
+ self.makeRandomId = function() {
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let id = "";
for (let count = 0; count < 16; ++count) {
@@ -109,16 +112,10 @@ export var tools = new function() {
return id;
};
- self.makeIdByText = function(text) {
+ self.makeTextId = function(text) {
return btoa(text).replace("=", "_");
};
- self.getRandomInt = function(min, max) {
- min = Math.ceil(min);
- max = Math.floor(max);
- return Math.floor(Math.random() * (max - min + 1)) + min;
- };
-
self.formatSize = function(size) {
if (size > 0) {
let index = Math.floor( Math.log(size) / Math.log(1024) );
@@ -149,6 +146,12 @@ export var tools = new function() {
return remapped;
};
+ self.getRandomInt = function(min, max) {
+ min = Math.ceil(min);
+ max = Math.floor(max);
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+ };
+
/************************************************************************/
self.el = new function() {
@@ -270,26 +273,34 @@ export var tools = new function() {
self.radio = new function() {
return {
"makeItem": function(name, title, value) {
+ let e_id = self.escape(name) + self.makeTextId(value);
return `
-
- ${title}
+
+
+ ${tools.escape(title)}
+
`;
},
"setOnClick": function(name, callback, prevent_default=true) {
- for (let el of $$$(`input[type="radio"][name="${name}"]`)) {
+ for (let el of $$$(`input[type="radio"][name="${CSS.escape(name)}"]`)) {
self.el.setOnClick(el, callback, prevent_default);
}
},
"getValue": function(name) {
- return document.querySelector(`input[type="radio"][name="${name}"]:checked`).value;
+ return document.querySelector(`input[type="radio"][name="${CSS.escape(name)}"]:checked`).value;
},
"setValue": function(name, value) {
- for (let el of $$$(`input[type="radio"][name="${name}"]`)) {
+ for (let el of $$$(`input[type="radio"][name="${CSS.escape(name)}"]`)) {
el.checked = (el.value === value);
}
},
"clickValue": function(name, value) {
- for (let el of $$$(`input[type="radio"][name="${name}"]`)) {
+ for (let el of $$$(`input[type="radio"][name="${CSS.escape(name)}"]`)) {
if (el.value === value) {
el.click();
return;
@@ -297,7 +308,7 @@ export var tools = new function() {
}
},
"setEnabled": function(name, enabled) {
- for (let el of $$$(`input[type="radio"][name="${name}"]`)) {
+ for (let el of $$$(`input[type="radio"][name="${CSS.escape(name)}"]`)) {
self.el.setEnabled(el, enabled);
}
},
diff --git a/web/share/js/vnc/main.js b/web/share/js/vnc/main.js
index 352826b7..2e60f17c 100644
--- a/web/share/js/vnc/main.js
+++ b/web/share/js/vnc/main.js
@@ -32,16 +32,26 @@ export function main() {
function __loadKvmdInfo() {
tools.httpGet("api/info", null, function(http) {
- if (http.status === 200) {
- let vnc_port = JSON.parse(http.responseText).result.extras.vnc.port;
- $("vnc-text").innerHTML = `
- vncviewer ${window.location.hostname}::${vnc_port}
- `;
- } else if (http.status === 401 || http.status === 403) {
- tools.currentOpen("login");
- } else {
- setTimeout(__loadKvmdInfo, 1000);
+ switch (http.status) {
+ case 200:
+ __showKvmdInfo(JSON.parse(http.responseText).result);
+ break;
+
+ case 401:
+ case 403:
+ tools.currentOpen("login");
+ break;
+
+ default:
+ setTimeout(__loadKvmdInfo, 1000);
+ break;
}
});
}
+
+function __showKvmdInfo(info) {
+ $("vnc-text").innerHTML = `
+ vncviewer ${tools.escape(window.location.hostname + "::" + info.extras.vnc.port)}
+ `;
+}
diff --git a/web/share/js/wm.js b/web/share/js/wm.js
index 162f43ba..60a323b1 100644
--- a/web/share/js/wm.js
+++ b/web/share/js/wm.js
@@ -42,96 +42,105 @@ function __WindowManager() {
var __menu_buttons = [];
var __init__ = function() {
- for (let el_button of $$$("button")) {
+ for (let el of $$$("button")) {
// XXX: Workaround for iOS Safari:
// https://stackoverflow.com/questions/3885018/active-pseudo-class-doesnt-work-in-mobile-safari
- el_button.ontouchstart = function() {};
+ el.ontouchstart = function() {};
}
- for (let el_button of $$("menu-button")) {
- el_button.parentElement.querySelector(".menu").setAttribute("tabindex", "-1");
- tools.el.setOnDown(el_button, () => __toggleMenu(el_button));
- __menu_buttons.push(el_button);
+ for (let el of $$("menu-button")) {
+ el.parentElement.querySelector(".menu").setAttribute("tabindex", "-1");
+ tools.el.setOnDown(el, () => __toggleMenu(el));
+ __menu_buttons.push(el);
}
if (!window.ResizeObserver) {
tools.error("ResizeObserver not supported");
}
- for (let el_window of $$("window")) {
- el_window.setAttribute("tabindex", "-1");
- __makeWindowMovable(el_window);
- __windows.push(el_window);
+ for (let el_win of $$("window")) {
+ el_win.setAttribute("tabindex", "-1");
+ __makeWindowMovable(el_win);
+ __windows.push(el_win);
- if (el_window.classList.contains("window-resizable") && window.ResizeObserver) {
+ if (el_win.classList.contains("window-resizable") && window.ResizeObserver) {
new ResizeObserver(function() {
// При переполнении рабочей области сократить размер окна по высоте.
// По ширине оно настраивается само в CSS.
let view = self.getViewGeometry();
- let rect = el_window.getBoundingClientRect();
+ let rect = el_win.getBoundingClientRect();
if ((rect.bottom - rect.top) > (view.bottom - view.top)) {
let ratio = (rect.bottom - rect.top) / (view.bottom - view.top);
- el_window.style.height = view.bottom - view.top + "px";
- el_window.style.width = Math.round((rect.right - rect.left) / ratio) + "px";
+ el_win.style.height = view.bottom - view.top + "px";
+ el_win.style.width = Math.round((rect.right - rect.left) / ratio) + "px";
}
- if (el_window.hasAttribute("data-centered")) {
- __centerWindow(el_window);
+ if (el_win.hasAttribute("data-centered")) {
+ __centerWindow(el_win);
}
- }).observe(el_window);
+ }).observe(el_win);
}
- let el_close_button = el_window.querySelector(".window-header .window-button-close");
- if (el_close_button) {
- el_close_button.title = "Close window";
- tools.el.setOnClick(el_close_button, () => self.closeWindow(el_window));
+ {
+ let el = el_win.querySelector(".window-header .window-button-close");
+ if (el) {
+ el.title = "Close window";
+ tools.el.setOnClick(el, () => self.closeWindow(el_win));
+ }
}
- let el_maximize_button = el_window.querySelector(".window-header .window-button-maximize");
- if (el_maximize_button) {
- el_maximize_button.title = "Maximize window";
- tools.el.setOnClick(el_maximize_button, function() {
- __maximizeWindow(el_window);
- __activateLastWindow(el_window);
- });
+ {
+ let el = el_win.querySelector(".window-header .window-button-maximize");
+ if (el) {
+ el.title = "Maximize window";
+ tools.el.setOnClick(el, function() {
+ __maximizeWindow(el_win);
+ __activateLastWindow(el_win);
+ });
+ }
}
- let el_orig_button = el_window.querySelector(".window-header .window-button-original");
- if (el_orig_button) {
- el_orig_button.title = "Reduce window to its original size and center it";
- tools.el.setOnClick(el_orig_button, function() {
- el_window.style.width = "";
- el_window.style.height = "";
- __centerWindow(el_window);
- __activateLastWindow(el_window);
- });
+ {
+ let el = el_win.querySelector(".window-header .window-button-original");
+ if (el) {
+ el.title = "Reduce window to its original size and center it";
+ tools.el.setOnClick(el, function() {
+ el_win.style.width = "";
+ el_win.style.height = "";
+ __centerWindow(el_win);
+ __activateLastWindow(el_win);
+ });
+ }
}
- let el_enter_full_tab_button = el_window.querySelector(".window-header .window-button-enter-full-tab");
- let el_exit_full_tab_button = el_window.querySelector(".window-button-exit-full-tab");
- if (el_enter_full_tab_button && el_exit_full_tab_button) {
- el_enter_full_tab_button.title = "Stretch to the entire tab";
- tools.el.setOnClick(el_enter_full_tab_button, () => self.setFullTabWindow(el_window, true));
- tools.el.setOnClick(el_exit_full_tab_button, () => self.setFullTabWindow(el_window, false));
+ {
+ let el_enter = el_win.querySelector(".window-header .window-button-enter-full-tab");
+ let el_exit = el_win.querySelector(".window-button-exit-full-tab");
+ if (el_enter && el_exit) {
+ el_enter.title = "Stretch to the entire tab";
+ tools.el.setOnClick(el_enter, () => self.setFullTabWindow(el_win, true));
+ tools.el.setOnClick(el_exit, () => self.setFullTabWindow(el_win, false));
+ }
}
- let el_full_screen_button = el_window.querySelector(".window-header .window-button-full-screen");
- if (el_full_screen_button && __getFullScreenFunction(el_window)) {
- el_full_screen_button.title = "Go to full-screen mode";
- tools.el.setOnClick(el_full_screen_button, function() {
- __fullScreenWindow(el_window);
- el_window.focus(el_window); // Почему-то теряется фокус
- __activateLastWindow(el_window);
- });
+ {
+ let el = el_win.querySelector(".window-header .window-button-full-screen");
+ if (el && __getFullScreenFunction(el_win)) {
+ el.title = "Go to full-screen mode";
+ tools.el.setOnClick(el, function() {
+ __fullScreenWindow(el_win);
+ el_win.focus(el_win); // Почему-то теряется фокус
+ __activateLastWindow(el_win);
+ });
+ }
}
}
- for (let el_button of $$$("button[data-show-window]")) {
- tools.el.setOnClick(el_button, () => self.showWindow($(el_button.getAttribute("data-show-window"))));
+ for (let el of $$$("button[data-show-window]")) {
+ tools.el.setOnClick(el, () => self.showWindow($(el.getAttribute("data-show-window"))));
}
- window.onmouseup = __globalMouseButtonHandler;
- window.ontouchend = __globalMouseButtonHandler;
+ window.onmouseup = window.ontouchend = __globalMouseButtonHandler;
window.addEventListener("focusin", (event) => __focusInOut(event, true));
window.addEventListener("focusout", (event) => __focusInOut(event, false));
@@ -196,7 +205,12 @@ function __WindowManager() {
var __modalCodeDialog = function(header, html, code, ok, cancel) {
let create_content = function(el_content) {
if (code) {
- html += ` `;
+ html += `
+
+
+
${tools.escape(code)}
+
+ `;
}
el_content.innerHTML = html;
};
@@ -210,49 +224,49 @@ function __WindowManager() {
el_modal.className = "modal";
el_modal.style.visibility = "visible";
- let el_window = document.createElement("div");
- el_window.className = "modal-window";
- el_window.setAttribute("tabindex", "-1");
- el_modal.appendChild(el_window);
+ let el_win = document.createElement("div");
+ el_win.className = "modal-window";
+ el_win.setAttribute("tabindex", "-1");
+ el_modal.appendChild(el_win);
let el_header = document.createElement("div");
el_header.className = "modal-header";
el_header.innerText = header;
- el_window.appendChild(el_header);
+ el_win.appendChild(el_header);
let el_content = document.createElement("div");
el_content.className = "modal-content";
- el_window.appendChild(el_content);
+ el_win.appendChild(el_content);
let el_buttons = document.createElement("div");
el_buttons.classList.add("modal-buttons", "buttons-row");
- el_window.appendChild(el_buttons);
+ el_win.appendChild(el_buttons);
- let el_cancel_button = null;
- let el_ok_button = null;
+ let el_cancel_bt = null;
+ let el_ok_bt = null;
if (cancel) {
- el_cancel_button = document.createElement("button");
- el_cancel_button.className = "row100";
- el_cancel_button.innerText = "Cancel";
- el_buttons.appendChild(el_cancel_button);
+ el_cancel_bt = document.createElement("button");
+ el_cancel_bt.className = "row100";
+ el_cancel_bt.innerText = "Cancel";
+ el_buttons.appendChild(el_cancel_bt);
}
if (ok) {
- el_ok_button = document.createElement("button");
- el_ok_button.className = "row100";
- el_ok_button.innerText = "OK";
- el_buttons.appendChild(el_ok_button);
+ el_ok_bt = document.createElement("button");
+ el_ok_bt.className = "row100";
+ el_ok_bt.innerText = "OK";
+ el_buttons.appendChild(el_ok_bt);
}
if (ok && cancel) {
- el_ok_button.className = "row50";
- el_cancel_button.className = "row50";
+ el_ok_bt.className = "row50";
+ el_cancel_bt.className = "row50";
}
- el_window.onkeyup = function(event) {
+ el_win.onkeyup = function(event) {
event.preventDefault();
if (ok && event.code === "Enter") {
- el_ok_button.click();
+ el_ok_bt.click();
} else if (cancel && event.code === "Escape") {
- el_cancel_button.click();
+ el_cancel_bt.click();
}
};
@@ -260,7 +274,7 @@ function __WindowManager() {
if (ok || cancel) {
promise = new Promise(function(resolve) {
function close(retval) {
- __closeWindow(el_window);
+ __closeWindow(el_win);
let index = __windows.indexOf(el_modal);
if (index !== -1) {
__windows.splice(index, 1);
@@ -276,10 +290,10 @@ function __WindowManager() {
}
if (cancel) {
- tools.el.setOnClick(el_cancel_button, () => close(false));
+ tools.el.setOnClick(el_cancel_bt, () => close(false));
}
if (ok) {
- tools.el.setOnClick(el_ok_button, () => close(true));
+ tools.el.setOnClick(el_ok_bt, () => close(true));
}
});
}
@@ -288,7 +302,7 @@ function __WindowManager() {
(parent || document.fullscreenElement || document.body).appendChild(el_modal);
if (typeof html === "function") {
// Это должно быть здесь, потому что элемент должен иметь родителя чтобы существовать
- html(el_content, el_ok_button);
+ html(el_content, el_ok_bt);
} else {
el_content.innerHTML = html;
}
@@ -297,26 +311,26 @@ function __WindowManager() {
return promise;
};
- self.showWindow = function(el_window, activate=true, center=false) {
+ self.showWindow = function(el_win, activate=true, center=false) {
let showed = false;
- if (!self.isWindowVisible(el_window)) {
+ if (!self.isWindowVisible(el_win)) {
center = true;
showed = true;
}
- __organizeWindow(el_window, center);
- el_window.style.visibility = "visible";
+ __organizeWindow(el_win, center);
+ el_win.style.visibility = "visible";
if (activate) {
- __activateWindow(el_window);
+ __activateWindow(el_win);
}
- if (el_window.show_hook) {
+ if (el_win.show_hook) {
if (showed) {
- el_window.show_hook();
+ el_win.show_hook();
}
}
};
- self.isWindowVisible = function(el_window) {
- return (window.getComputedStyle(el_window, null).visibility !== "hidden");
+ self.isWindowVisible = function(el_win) {
+ return (window.getComputedStyle(el_win, null).visibility !== "hidden");
};
self.getViewGeometry = function() {
@@ -329,35 +343,35 @@ function __WindowManager() {
};
};
- self.closeWindow = function(el_window) {
- __closeWindow(el_window);
- __activateLastWindow(el_window);
+ self.closeWindow = function(el_win) {
+ __closeWindow(el_win);
+ __activateLastWindow(el_win);
};
- self.setFullTabWindow = function(el_window, enabled) {
- el_window.classList.toggle("window-full-tab", enabled);
- __activateLastWindow(el_window);
+ self.setFullTabWindow = function(el_win, enabled) {
+ el_win.classList.toggle("window-full-tab", enabled);
+ __activateLastWindow(el_win);
let el_navbar = $("navbar");
if (el_navbar) {
tools.hidden.setVisible(el_navbar, !enabled);
}
};
- var __closeWindow = function(el_window) {
- el_window.focus();
- el_window.blur();
- el_window.style.visibility = "hidden";
- if (el_window.close_hook) {
- el_window.close_hook();
+ var __closeWindow = function(el_win) {
+ el_win.focus();
+ el_win.blur();
+ el_win.style.visibility = "hidden";
+ if (el_win.close_hook) {
+ el_win.close_hook();
}
};
var __toggleMenu = function(el_a) {
let all_hidden = true;
- for (let el_button of __menu_buttons) {
- let el_menu = el_button.parentElement.querySelector(".menu");
- if (el_button === el_a && window.getComputedStyle(el_menu, null).visibility === "hidden") {
+ for (let el_bt of __menu_buttons) {
+ let el_menu = el_bt.parentElement.querySelector(".menu");
+ if (el_bt === el_a && window.getComputedStyle(el_menu, null).visibility === "hidden") {
let rect = el_menu.getBoundingClientRect();
let offset = self.getViewGeometry().right - (rect.left + el_menu.clientWidth + 2); // + 2 is ugly hack
if (offset < 0) {
@@ -366,13 +380,13 @@ function __WindowManager() {
el_menu.style.removeProperty("right");
}
- el_button.classList.add("menu-button-pressed");
+ el_bt.classList.add("menu-button-pressed");
el_menu.style.visibility = "visible";
let el_focus = el_menu.querySelector("[data-focus]");
(el_focus !== null ? el_focus : el_menu).focus();
all_hidden &= false;
} else {
- el_button.classList.remove("menu-button-pressed");
+ el_bt.classList.remove("menu-button-pressed");
el_menu.style.visibility = "hidden";
el_menu.style.removeProperty("right");
}
@@ -394,9 +408,9 @@ function __WindowManager() {
var __closeAllMenues = function() {
document.onkeyup = null;
- for (let el_button of __menu_buttons) {
- let el_menu = el_button.parentElement.querySelector(".menu");
- el_button.classList.remove("menu-button-pressed");
+ for (let el_bt of __menu_buttons) {
+ let el_menu = el_bt.parentElement.querySelector(".menu");
+ el_bt.classList.remove("menu-button-pressed");
el_menu.style.visibility = "hidden";
el_menu.style.removeProperty("right");
}
@@ -420,10 +434,10 @@ function __WindowManager() {
&& !event.target.closest(".menu-button")
&& !event.target.closest(".modal")
) {
- for (let el_item = event.target; el_item && el_item !== document; el_item = el_item.parentNode) {
- if (el_item.classList.contains("menu")) {
+ for (let el = event.target; el && el !== document; el = el.parentNode) {
+ if (el.classList.contains("menu")) {
return;
- } else if (el_item.hasAttribute("data-force-hide-menu")) {
+ } else if (el.hasAttribute("data-force-hide-menu")) {
break;
}
}
@@ -433,122 +447,122 @@ function __WindowManager() {
};
var __organizeWindowsOnBrowserResize = function() {
- for (let el_window of $$("window")) {
- if (el_window.style.visibility === "visible") {
- if (tools.browser.is_mobile && el_window.classList.contains("window-resizable")) {
+ for (let el_win of $$("window")) {
+ if (el_win.style.visibility === "visible") {
+ if (tools.browser.is_mobile && el_win.classList.contains("window-resizable")) {
// FIXME: При смене ориентации на мобильном браузере надо сбрасывать
// настройки окна стрима, поэтому тут стоит вот этот костыль
- el_window.style.width = "";
- el_window.style.height = "";
+ el_win.style.width = "";
+ el_win.style.height = "";
}
- __organizeWindow(el_window);
+ __organizeWindow(el_win);
}
}
};
- var __organizeWindow = function(el_window, center=false) {
+ var __organizeWindow = function(el_win, center=false) {
let view = self.getViewGeometry();
- let rect = el_window.getBoundingClientRect();
+ let rect = el_win.getBoundingClientRect();
- if (el_window.classList.contains("window-resizable")) {
+ if (el_win.classList.contains("window-resizable")) {
// При переполнении рабочей области сократить размер окна
if ((rect.bottom - rect.top) > (view.bottom - view.top)) {
let ratio = (rect.bottom - rect.top) / (view.bottom - view.top);
- el_window.style.height = view.bottom - view.top + "px";
- el_window.style.width = Math.round((rect.right - rect.left) / ratio) + "px";
+ el_win.style.height = view.bottom - view.top + "px";
+ el_win.style.width = Math.round((rect.right - rect.left) / ratio) + "px";
}
if ((rect.right - rect.left) > (view.right - view.left)) {
- el_window.style.width = view.right - view.left + "px";
+ el_win.style.width = view.right - view.left + "px";
}
- rect = el_window.getBoundingClientRect();
+ rect = el_win.getBoundingClientRect();
}
- if (el_window.hasAttribute("data-centered") || center) {
- __centerWindow(el_window);
+ if (el_win.hasAttribute("data-centered") || center) {
+ __centerWindow(el_win);
} else {
if (rect.top <= view.top) {
- el_window.style.top = view.top + "px";
+ el_win.style.top = view.top + "px";
} else if (rect.bottom > view.bottom) {
- el_window.style.top = view.bottom - rect.height + "px";
+ el_win.style.top = view.bottom - rect.height + "px";
}
if (rect.left <= view.left) {
- el_window.style.left = view.left + "px";
+ el_win.style.left = view.left + "px";
} else if (rect.right > view.right) {
- el_window.style.left = view.right - rect.width + "px";
+ el_win.style.left = view.right - rect.width + "px";
}
}
};
- var __centerWindow = function(el_window) {
+ var __centerWindow = function(el_win) {
let view = self.getViewGeometry();
- let rect = el_window.getBoundingClientRect();
- el_window.style.top = Math.max(view.top, Math.round((view.bottom - rect.height) / 2)) + "px";
- el_window.style.left = Math.round((view.right - rect.width) / 2) + "px";
- el_window.setAttribute("data-centered", "");
+ let rect = el_win.getBoundingClientRect();
+ el_win.style.top = Math.max(view.top, Math.round((view.bottom - rect.height) / 2)) + "px";
+ el_win.style.left = Math.round((view.right - rect.width) / 2) + "px";
+ el_win.setAttribute("data-centered", "");
};
- var __activateLastWindow = function(el_except_window=null) {
- let el_last_window = null;
+ var __activateLastWindow = function(el_except_win=null) {
+ let el_last_win = null;
if (document.activeElement) {
- el_last_window = (document.activeElement.closest(".modal-window") || document.activeElement.closest(".window"));
- if (el_last_window && window.getComputedStyle(el_last_window, null).visibility === "hidden") {
- el_last_window = null;
+ el_last_win = (document.activeElement.closest(".modal-window") || document.activeElement.closest(".window"));
+ if (el_last_win && window.getComputedStyle(el_last_win, null).visibility === "hidden") {
+ el_last_win = null;
}
}
- if (!el_last_window || el_last_window === el_except_window) {
+ if (!el_last_win || el_last_win === el_except_win) {
let max_z_index = 0;
- for (let el_window of __windows) {
- let z_index = parseInt(window.getComputedStyle(el_window, null).zIndex) || 0;
- let visibility = window.getComputedStyle(el_window, null).visibility;
+ for (let el_win of __windows) {
+ let z_index = parseInt(window.getComputedStyle(el_win, null).zIndex) || 0;
+ let visibility = window.getComputedStyle(el_win, null).visibility;
- if (max_z_index < z_index && visibility !== "hidden" && el_window !== el_except_window) {
- el_last_window = el_window;
+ if (max_z_index < z_index && visibility !== "hidden" && el_win !== el_except_win) {
+ el_last_win = el_win;
max_z_index = z_index;
}
}
}
- if (el_last_window) {
- tools.debug("UI: Activating last window:", el_last_window);
- __activateWindow(el_last_window);
+ if (el_last_win) {
+ tools.debug("UI: Activating last window:", el_last_win);
+ __activateWindow(el_last_win);
} else {
tools.debug("UI: No last window to activation");
}
};
- var __activateWindow = function(el_window) {
- if (window.getComputedStyle(el_window, null).visibility !== "hidden") {
+ var __activateWindow = function(el_win) {
+ if (window.getComputedStyle(el_win, null).visibility !== "hidden") {
let el_to_focus;
- let el_window_contains_focus;
+ let el_focused; // A window which contains a focus
- if (el_window.className === "modal") {
- el_to_focus = el_window.querySelector(".modal-window");
- el_window_contains_focus = (document.activeElement && document.activeElement.closest(".modal-window"));
+ if (el_win.className === "modal") {
+ el_to_focus = el_win.querySelector(".modal-window");
+ el_focused = (document.activeElement && document.activeElement.closest(".modal-window"));
} else { // .window
- el_to_focus = el_window;
- el_window_contains_focus = (document.activeElement && document.activeElement.closest(".window"));
+ el_to_focus = el_win;
+ el_focused = (document.activeElement && document.activeElement.closest(".window"));
}
- if (el_window.className !== "modal" && parseInt(el_window.style.zIndex) !== __top_z_index) {
+ if (el_win.className !== "modal" && parseInt(el_win.style.zIndex) !== __top_z_index) {
__top_z_index += 1;
- el_window.style.zIndex = __top_z_index;
- tools.debug("UI: Activated window:", el_window);
+ el_win.style.zIndex = __top_z_index;
+ tools.debug("UI: Activated window:", el_win);
}
- if (el_window !== el_window_contains_focus) {
+ if (el_win !== el_focused) {
el_to_focus.focus();
- tools.debug("UI: Focused window:", el_window);
+ tools.debug("UI: Focused window:", el_win);
}
}
};
- var __makeWindowMovable = function(el_window) {
- let el_header = el_window.querySelector(".window-header");
- let el_grab = el_window.querySelector(".window-header .window-grab");
+ var __makeWindowMovable = function(el_win) {
+ let el_header = el_win.querySelector(".window-header");
+ let el_grab = el_win.querySelector(".window-header .window-grab");
if (el_header === null || el_grab === null) {
// Для псевдоокна OCR
return;
@@ -559,10 +573,10 @@ function __WindowManager() {
function startMoving(event) {
// При перетаскивании resizable-окна за правый кран экрана оно ужимается.
// Этот костыль фиксит это.
- el_window.style.width = el_window.offsetWidth + "px";
+ el_win.style.width = el_win.offsetWidth + "px";
__closeAllMenues();
- __activateWindow(el_window);
+ __activateWindow(el_win);
event = (event || window.event);
event.preventDefault();
@@ -580,7 +594,7 @@ function __WindowManager() {
}
function doMoving(event) {
- el_window.removeAttribute("data-centered");
+ el_win.removeAttribute("data-centered");
event = (event || window.event);
event.preventDefault();
@@ -589,8 +603,8 @@ function __WindowManager() {
let x = prev_pos.x - event_pos.x;
let y = prev_pos.y - event_pos.y;
- el_window.style.top = (el_window.offsetTop - y) + "px";
- el_window.style.left = (el_window.offsetLeft - x) + "px";
+ el_win.style.top = (el_win.offsetTop - y) + "px";
+ el_win.style.left = (el_win.offsetLeft - x) + "px";
prev_pos = event_pos;
}
@@ -613,29 +627,29 @@ function __WindowManager() {
}
}
- el_window.setAttribute("data-centered", "");
- el_window.onmousedown = el_window.ontouchstart = () => __activateWindow(el_window);
+ el_win.setAttribute("data-centered", "");
+ el_win.onmousedown = el_win.ontouchstart = () => __activateWindow(el_win);
el_grab.onmousedown = startMoving;
el_grab.ontouchstart = startMoving;
};
var __onFullScreenChange = function(event) {
- let el_window = event.target;
+ let el_win = event.target;
if (!document.fullscreenElement) {
- let rect = el_window.before_full_screen;
+ let rect = el_win.before_full_screen;
if (rect) {
- el_window.style.width = rect.width + "px";
- el_window.style.height = rect.height + "px";
- el_window.style.top = rect.top + "px";
- el_window.style.left = rect.left + "px";
+ el_win.style.width = rect.width + "px";
+ el_win.style.height = rect.height + "px";
+ el_win.style.top = rect.top + "px";
+ el_win.style.left = rect.left + "px";
}
}
};
- var __fullScreenWindow = function(el_window) {
- el_window.before_full_screen = el_window.getBoundingClientRect();
- __getFullScreenFunction(el_window).call(el_window);
+ var __fullScreenWindow = function(el_win) {
+ el_win.before_full_screen = el_win.getBoundingClientRect();
+ __getFullScreenFunction(el_win).call(el_win);
if (navigator.keyboard && navigator.keyboard.lock) {
navigator.keyboard.lock();
} else {
@@ -647,26 +661,26 @@ function __WindowManager() {
+ "In Chrome use HTTPS and enable system-keyboard-lock "
+ "by putting at URL chrome://flags/#system-keyboard-lock "
);
- __modalDialog("Keyboard lock is unsupported", msg, true, false, el_window);
+ __modalDialog("Keyboard lock is unsupported", msg, true, false, el_win);
}
};
- var __maximizeWindow = function(el_window) {
+ var __maximizeWindow = function(el_win) {
let el_navbar = $("navbar");
let vertical_offset = (el_navbar ? el_navbar.offsetHeight : 0);
- el_window.style.left = "0px";
- el_window.style.top = vertical_offset + "px";
- el_window.style.width = window.innerWidth + "px";
- el_window.style.height = window.innerHeight - vertical_offset + "px";
+ el_win.style.left = "0px";
+ el_win.style.top = vertical_offset + "px";
+ el_win.style.width = window.innerWidth + "px";
+ el_win.style.height = window.innerHeight - vertical_offset + "px";
};
- var __getFullScreenFunction = function(el_window) {
- if (el_window.requestFullscreen) {
- return el_window.requestFullscreen;
- } else if (el_window.webkitRequestFullscreen) {
- return el_window.webkitRequestFullscreen;
- } else if (el_window.mozRequestFullscreen) {
- return el_window.mozRequestFullscreen;
+ var __getFullScreenFunction = function(el_win) {
+ if (el_win.requestFullscreen) {
+ return el_win.requestFullscreen;
+ } else if (el_win.webkitRequestFullscreen) {
+ return el_win.webkitRequestFullscreen;
+ } else if (el_win.mozRequestFullscreen) {
+ return el_win.mozRequestFullscreen;
}
return null;
};