refactoring

This commit is contained in:
Devaev Maxim
2018-11-14 02:52:00 +03:00
parent 49a09a6ba1
commit 2916c7fe06
23 changed files with 332 additions and 331 deletions

63
web/js/kvm/atx.js Normal file
View File

@@ -0,0 +1,63 @@
function Atx() {
var self = this;
/********************************************************************************/
var __init__ = function() {
$("atx-power-led").title = "Power Led";
$("atx-hdd-led").title = "Disk Activity Led";
tools.setOnClick($("atx-power-button"), () => __clickButton("power", null, "Are you sure to click the power button?"));
tools.setOnClick($("atx-power-button-long"), () => __clickButton("power_long", 15000, "Are you sure to perform the long press of the power button?"));
tools.setOnClick($("atx-reset-button"), () => __clickButton("reset", null, "Are you sure to reboot the server?"));
};
/********************************************************************************/
self.loadInitialState = function() {
var http = tools.makeRequest("GET", "/kvmd/atx", function() {
if (http.readyState === 4) {
if (http.status === 200) {
__setButtonsBusy(JSON.parse(http.responseText).result.busy);
} else {
setTimeout(self.loadInitialState, 1000);
}
}
});
};
self.setState = function(state) {
__setButtonsBusy(state.busy);
$("atx-power-led").className = (state.leds.power ? "led-green" : "led-gray");
$("atx-hdd-led").className = (state.leds.hdd ? "led-red" : "led-gray");
};
self.clearState = function() {
$("atx-power-led").className = "led-gray";
$("atx-hdd-led").className = "led-gray";
};
var __clickButton = function(button, timeout, confirm_msg) {
ui.confirm(confirm_msg).then(function(ok) {
if (ok) {
var http = tools.makeRequest("POST", "/kvmd/atx/click?button=" + button, function() {
if (http.readyState === 4) {
if (http.status === 409) {
ui.error("Performing another ATX operation for other client.<br>Please try again later");
} else if (http.status !== 200) {
ui.error("Click error:<br>", http.responseText);
}
}
}, timeout);
}
});
};
var __setButtonsBusy = function(busy) {
$("atx-power-button").disabled = busy;
$("atx-power-button-long").disabled = busy;
$("atx-reset-button").disabled = busy;
};
__init__();
}

195
web/js/kvm/hid.js Normal file
View File

@@ -0,0 +1,195 @@
function Hid() {
var self = this;
/********************************************************************************/
var __ws = null;
var __chars_to_codes = {};
var __codes_delay = 50;
var __keyboard = new Keyboard();
var __mouse = new Mouse();
var __init__ = function() {
var __hidden_attr = null;
var __visibility_change_attr = null;
if (typeof document.hidden !== "undefined") {
__hidden_attr = "hidden";
__visibility_change_attr = "visibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
__hidden_attr = "webkitHidden";
__visibility_change_attr = "webkitvisibilitychange";
} else if (typeof document.mozHidden !== "undefined") {
__hidden_attr = "mozHidden";
__visibility_change_attr = "mozvisibilitychange";
}
if (__visibility_change_attr) {
document.addEventListener(
__visibility_change_attr,
function() {
if (document[__hidden_attr]) {
__releaseAll();
}
},
false
);
}
window.onpagehide = __releaseAll;
window.onblur = __releaseAll;
__chars_to_codes = __buildCharsToCodes();
tools.setOnClick($("hid-pak-button"), __clickPasteAsKeysButton);
tools.setOnClick($("hid-reset-button"), __clickResetButton);
Array.prototype.forEach.call(document.querySelectorAll("[data-shortcut]"), function(el_shortcut) {
tools.setOnClick(el_shortcut, () => __emitShortcut(el_shortcut.getAttribute("data-shortcut").split(" ")));
});
};
/********************************************************************************/
self.setSocket = function(ws) {
__ws = ws;
__keyboard.setSocket(ws);
__mouse.setSocket(ws);
$("hid-pak-text").disabled = $("hid-pak-button").disabled = $("hid-reset-button").disabled = !ws;
};
var __releaseAll = function() {
__keyboard.releaseAll();
};
var __emitShortcut = function(codes) {
return new Promise(function(resolve) {
tools.debug("HID: emitting keys:", codes);
var raw_events = [];
[[codes, true], [codes.slice().reverse(), false]].forEach(function(op) {
var [op_codes, state] = op;
op_codes.forEach(function(code) {
raw_events.push({code: code, state: state});
});
});
var index = 0;
var iterate = () => setTimeout(function() {
__keyboard.fireEvent(raw_events[index].code, raw_events[index].state);
++index;
if (index < raw_events.length) {
iterate();
} else {
resolve(null);
}
}, __codes_delay);
iterate();
});
};
var __buildCharsToCodes = function() {
var chars_to_codes = {
"\n": ["Enter"],
"\t": ["Tab"],
" ": ["Space"],
"`": ["Backquote"], "~": ["ShiftLeft", "Backquote"],
"\\": ["Backslash"], "|": ["ShiftLeft", "Backslash"],
"[": ["BracketLeft"], "{": ["ShiftLeft", "BracketLeft"],
"]": ["BracketLeft"], "}": ["ShiftLeft", "BracketRight"],
",": ["Comma"], "<": ["ShiftLeft", "Comma"],
".": ["Period"], ">": ["ShiftLeft", "Period"],
"1": ["Digit1"], "!": ["ShiftLeft", "Digit1"],
"2": ["Digit2"], "@": ["ShiftLeft", "Digit2"],
"3": ["Digit3"], "#": ["ShiftLeft", "Digit3"],
"4": ["Digit4"], "$": ["ShiftLeft", "Digit4"],
"5": ["Digit5"], "%": ["ShiftLeft", "Digit5"],
"6": ["Digit6"], "^": ["ShiftLeft", "Digit6"],
"7": ["Digit7"], "&": ["ShiftLeft", "Digit7"],
"8": ["Digit8"], "*": ["ShiftLeft", "Digit8"],
"9": ["Digit9"], "(": ["ShiftLeft", "Digit9"],
"0": ["Digit0"], ")": ["ShiftLeft", "Digit0"],
"-": ["Minus"], "_": ["ShiftLeft", "Minus"],
"'": ["Quote"], "\"": ["ShiftLeft", "Quote"],
";": ["Semicolon"], ":": ["ShiftLeft", "Semicolon"],
"/": ["Slash"], "?": ["ShiftLeft", "Slash"],
"=": ["Equal"], "+": ["ShiftLeft", "Equal"],
};
for (var ch = "a".charCodeAt(0); ch <= "z".charCodeAt(0); ++ch) {
var low = String.fromCharCode(ch);
var up = low.toUpperCase();
var code = "Key" + up;
chars_to_codes[low] = [code];
chars_to_codes[up] = ["ShiftLeft", code];
}
return chars_to_codes;
};
var __clickPasteAsKeysButton = function() {
var text = $("hid-pak-text").value.replace(/[^\x00-\x7F]/g, ""); // eslint-disable-line no-control-regex
if (text) {
var clipboard_codes = [];
var codes_count = 0;
[...text].forEach(function(ch) {
var codes = __chars_to_codes[ch];
if (codes) {
codes_count += codes.length;
clipboard_codes.push(codes);
}
});
var confirm_msg = (
"You are going to automatically type " + codes_count
+ " characters from the system clipboard."
+ " It will take " + (__codes_delay * codes_count * 2 / 1000) + " seconds.<br>"
+ "<br>Are you sure you want to continue?<br>"
);
ui.confirm(confirm_msg).then(function(ok) {
if (ok) {
$("hid-pak-text").disabled = true;
$("hid-pak-button").disabled = true;
$("hid-pak-led").className = "led-yellow-rotating-fast";
$("hid-pak-led").title = "Autotyping...";
tools.debug("HID: paste-as-keys:", text);
var index = 0;
var iterate = function() {
__emitShortcut(clipboard_codes[index]).then(function() {
++index;
if (index < clipboard_codes.length && __ws) {
iterate();
} else {
$("hid-pak-text").value = "";
$("hid-pak-text").disabled = false;
$("hid-pak-button").disabled = false;
$("hid-pak-led").className = "led-gray";
$("hid-pak-led").title = "";
}
});
};
iterate();
} else {
$("hid-pak-text").value = "";
}
});
}
};
var __clickResetButton = function() {
var http = tools.makeRequest("POST", "/kvmd/hid/reset", function() {
if (http.readyState === 4) {
if (http.status !== 200) {
ui.error("HID reset error:<br>", http.responseText);
}
}
});
};
__init__();
}

187
web/js/kvm/keyboard.js Normal file
View File

@@ -0,0 +1,187 @@
function Keyboard() {
var self = this;
/********************************************************************************/
var __ws = null;
var __keys = [].slice.call(document.querySelectorAll("div#keyboard-desktop div.keyboard-block div.keyboard-row div.key"));
var __modifiers = [].slice.call(document.querySelectorAll("div#keyboard-desktop div.keyboard-block div.keyboard-row div.modifier"));
var __mac_cmd_hook = ((
window.navigator.oscpu
|| window.navigator.platform
|| window.navigator.appVersion
|| "Unknown"
).indexOf("Mac") !== -1);
var __init__ = function() {
$("hid-keyboard-led").title = "Keyboard free";
$("keyboard-window").onkeydown = (event) => __keyboardHandler(event, true);
$("keyboard-window").onkeyup = (event) => __keyboardHandler(event, false);
$("keyboard-window").onfocus = __updateLeds;
$("keyboard-window").onblur = __updateLeds;
$("stream-window").onkeydown = (event) => __keyboardHandler(event, true);
$("stream-window").onkeyup = (event) => __keyboardHandler(event, false);
$("stream-window").onfocus = __updateLeds;
$("stream-window").onblur = __updateLeds;
Array.prototype.forEach.call($$("key"), function(el_key) {
tools.setOnDown(el_key, () => __clickHandler(el_key, true));
tools.setOnUp(el_key, () => __clickHandler(el_key, false));
el_key.onmouseout = function() {
if (__isPressed(el_key)) {
__clickHandler(el_key, false);
}
};
});
Array.prototype.forEach.call($$("modifier"), function(el_key) {
tools.setOnDown(el_key, () => __toggleModifierHandler(el_key));
});
if (__mac_cmd_hook) {
tools.info("Keyboard: enabled Mac-CMD-Hook");
}
};
/********************************************************************************/
self.setSocket = function(ws) {
if (ws !== __ws) {
self.releaseAll();
__ws = ws;
}
__updateLeds();
};
self.releaseAll = function() {
__keys.concat(__modifiers).forEach(function(el_key) {
if (__isActive(el_key)) {
self.fireEvent(el_key.getAttribute("data-key"), false);
}
});
};
self.fireEvent = function(code, state) {
__keyboardHandler({code: code}, state);
};
var __updateLeds = function() {
if (__ws && (document.activeElement === $("stream-window") || document.activeElement === $("keyboard-window"))) {
$("hid-keyboard-led").className = "led-green";
$("hid-keyboard-led").title = "Keyboard captured";
} else {
$("hid-keyboard-led").className = "led-gray";
$("hid-keyboard-led").title = "Keyboard free";
}
};
var __keyboardHandler = function(event, state) {
if (event.preventDefault) {
event.preventDefault();
}
var el_key = document.querySelector("[data-key='" + event.code + "']");
if (el_key && !event.repeat) {
__commonHandler(el_key, state, "pressed");
if (__mac_cmd_hook) {
// https://bugs.chromium.org/p/chromium/issues/detail?id=28089
// https://bugzilla.mozilla.org/show_bug.cgi?id=1299553
if ((event.code === "MetaLeft" || event.code === "MetaRight") && !state) {
__keys.forEach(function(el_key) {
if (__isActive(el_key)) {
self.fireEvent(el_key.getAttribute("data-key"), false);
}
});
}
}
__unholdModifiers();
}
};
var __clickHandler = function(el_key, state) {
__commonHandler(el_key, state, "pressed");
__unholdModifiers();
};
var __toggleModifierHandler = function(el_key) {
__commonHandler(el_key, !__isActive(el_key), "holded");
};
var __unholdModifiers = function() {
__modifiers.forEach(function(el_key) {
if (__isHolded(el_key)) {
__deactivate(el_key);
__sendKey(el_key, false);
}
});
};
var __commonHandler = function(el_key, state, cls) {
if (state && !__isActive(el_key)) {
__deactivate(el_key);
__activate(el_key, cls);
__sendKey(el_key, true);
} else {
__deactivate(el_key);
__sendKey(el_key, false);
}
};
var __isPressed = function(el_key) {
var is_pressed = false;
Array.prototype.forEach.call(__resolveKeys(el_key), function(el_key) {
is_pressed = (is_pressed || el_key.classList.contains("pressed"));
});
return is_pressed;
};
var __isHolded = function(el_key) {
var is_holded = false;
Array.prototype.forEach.call(__resolveKeys(el_key), function(el_key) {
is_holded = (is_holded || el_key.classList.contains("holded"));
});
return is_holded;
};
var __isActive = function(el_key) {
var is_active = false;
Array.prototype.forEach.call(__resolveKeys(el_key), function(el_key) {
is_active = (is_active || el_key.classList.contains("pressed") || el_key.classList.contains("holded"));
});
return is_active;
};
var __activate = function(el_key, cls) {
Array.prototype.forEach.call(__resolveKeys(el_key), function(el_key) {
el_key.classList.add(cls);
});
};
var __deactivate = function(el_key) {
Array.prototype.forEach.call(__resolveKeys(el_key), function(el_key) {
el_key.classList.remove("pressed");
el_key.classList.remove("holded");
});
};
var __resolveKeys = function(el_key) {
return document.querySelectorAll("[data-key='" + el_key.getAttribute("data-key") + "']");
};
var __sendKey = function(el_key, state) {
var code = el_key.getAttribute("data-key");
tools.debug("Keyboard: key", (state ? "pressed:" : "released:"), code);
if (__ws) {
__ws.send(JSON.stringify({
event_type: "key",
key: code,
state: state,
}));
}
};
__init__();
}

23
web/js/kvm/main.js Normal file
View File

@@ -0,0 +1,23 @@
var ui;
function main() {
if (
!window.navigator
|| window.navigator.userAgent.indexOf("MSIE ") > 0
|| window.navigator.userAgent.indexOf("Trident/") > 0
|| window.navigator.userAgent.indexOf("Edge/") > 0
) {
$("bad-browser-modal").style.visibility = "visible";
} else {
ui = new Ui();
tools.setOnClick($("show-about-button"), () => ui.showWindow($("about-window")));
tools.setOnClick($("show-keyboard-button"), () => ui.showWindow($("keyboard-window")));
tools.setOnClick($("show-stream-button"), () => ui.showWindow($("stream-window")));
tools.setOnClick($("open-log-button"), () => ui.open("kvmd/log?seek=3600&follow=1", "_blank"));
ui.showWindow($("stream-window"));
new Session();
}
}

159
web/js/kvm/mouse.js Normal file
View File

@@ -0,0 +1,159 @@
function Mouse() {
var self = this;
/********************************************************************************/
var __ws = null;
var __current_pos = {x: 0, y:0};
var __sent_pos = {x: 0, y:0};
var __wheel_delta = {x: 0, y: 0};
var __stream_hovered = false;
var __init__ = function() {
$("hid-mouse-led").title = "Mouse free";
$("stream-box").onmouseenter = __hoverStream;
$("stream-box").onmouseleave = __leaveStream;
$("stream-box").onmousedown = (event) => __buttonHandler(event, true);
$("stream-box").onmouseup = (event) => __buttonHandler(event, false);
$("stream-box").oncontextmenu = (event) => event.preventDefault();
$("stream-box").onmousemove = __moveHandler;
$("stream-box").onwheel = __wheelHandler;
$("stream-box").ontouchstart = (event) => __touchMoveHandler(event);
Array.prototype.forEach.call(document.querySelectorAll("[data-mouse-button]"), function(el_button) {
var button = el_button.getAttribute("data-mouse-button");
tools.setOnDown(el_button, () => __sendButton(button, true));
tools.setOnUp(el_button, () => __sendButton(button, false));
});
setInterval(__sendMove, 100);
};
/********************************************************************************/
self.setSocket = function(ws) {
__ws = ws;
if (ws) {
$("stream-box").classList.add("stream-box-mouse-enabled");
} else {
$("stream-box").classList.remove("stream-box-mouse-enabled");
}
__updateLeds();
};
var __hoverStream = function() {
__stream_hovered = true;
__updateLeds();
};
var __leaveStream = function() {
__stream_hovered = false;
__updateLeds();
};
var __updateLeds = function() {
if (__ws && (__stream_hovered || tools.browser.is_ios)) {
// Mouse is always available on iOS via touchscreen
$("hid-mouse-led").className = "led-green";
$("hid-mouse-led").title = "Mouse tracked";
} else {
$("hid-mouse-led").className = "led-gray";
$("hid-mouse-led").title = "Mouse free";
}
};
var __buttonHandler = function(event, state) {
// https://www.w3schools.com/jsref/event_button.asp
event.preventDefault();
switch (event.button) {
case 0: __sendButton("left", state); break;
case 2: __sendButton("right", state); break;
}
};
var __touchMoveHandler = function(event) {
event.preventDefault();
if (event.touches[0].target && event.touches[0].target.getBoundingClientRect) {
var rect = event.touches[0].target.getBoundingClientRect();
__current_pos = {
x: Math.round(event.touches[0].clientX - rect.left),
y: Math.round(event.touches[0].clientY - rect.top),
};
__sendMove();
}
};
var __moveHandler = function(event) {
var rect = event.target.getBoundingClientRect();
__current_pos = {
x: Math.round(event.clientX - rect.left),
y: Math.round(event.clientY - rect.top),
};
};
var __sendButton = function(button, state) {
tools.debug("Mouse: button", (state ? "pressed:" : "released:"), button);
__sendMove();
if (__ws) {
__ws.send(JSON.stringify({
event_type: "mouse_button",
button: button,
state: state,
}));
}
};
var __sendMove = function() {
var pos = __current_pos;
if (pos.x !== __sent_pos.x || pos.y !== __sent_pos.y) {
var el_stream_image = $("stream-image");
var to = {
x: __translate(pos.x, 0, el_stream_image.clientWidth, -32768, 32767),
y: __translate(pos.y, 0, el_stream_image.clientHeight, -32768, 32767),
};
tools.debug("Mouse: moved:", to);
if (__ws) {
__ws.send(JSON.stringify({
event_type: "mouse_move",
to: to,
}));
}
__sent_pos = pos;
}
};
var __translate = function(x, a, b, c, d) {
return Math.round((x - a) / (b - a) * (d - c) + c);
};
var __wheelHandler = function(event) {
// https://learn.javascript.ru/mousewheel
if (event.preventDefault) {
event.preventDefault();
}
var delta = {x: 0, y: 0};
__wheel_delta.y += event.deltaY;
if (Math.abs(__wheel_delta.y) >= 100) {
delta.y = __wheel_delta.y / Math.abs(__wheel_delta.y) * (-5);
__wheel_delta.y = 0;
}
if (delta.y) {
tools.debug("Mouse: scrolled:", delta);
if (__ws) {
__ws.send(JSON.stringify({
event_type: "mouse_wheel",
delta: delta,
}));
}
}
};
__init__();
}

179
web/js/kvm/msd.js Normal file
View File

@@ -0,0 +1,179 @@
function Msd() {
var self = this;
/********************************************************************************/
var __state = null;
var __upload_http = null;
var __image_file = null;
var __init__ = function() {
$("msd-led").title = "Unknown state";
$("msd-select-new-image-file").onchange = __selectNewImageFile;
tools.setOnClick($("msd-select-new-image-button"), () => $("msd-select-new-image-file").click());
tools.setOnClick($("msd-upload-new-image-button"), __clickUploadNewImageButton);
tools.setOnClick($("msd-abort-uploading-button"), __clickAbortUploadingButton);
tools.setOnClick($("msd-switch-to-kvm-button"), () => __clickSwitchButton("kvm"));
tools.setOnClick($("msd-switch-to-server-button"), () => __clickSwitchButton("server"));
tools.setOnClick($("msd-reset-button"), __clickResetButton);
};
/********************************************************************************/
self.loadInitialState = function() {
var http = tools.makeRequest("GET", "/kvmd/msd", function() {
if (http.readyState === 4) {
if (http.status === 200) {
self.setState(JSON.parse(http.responseText).result);
} else {
setTimeout(self.loadInitialState, 1000);
}
}
});
};
self.setState = function(state) {
__state = state;
__applyState();
};
var __clickUploadNewImageButton = function() {
var form_data = new FormData();
form_data.append("image_name", __image_file.name);
form_data.append("image_data", __image_file);
__upload_http = new XMLHttpRequest();
__upload_http.open("POST", "/kvmd/msd/write", true);
__upload_http.upload.timeout = 5000;
__upload_http.onreadystatechange = __uploadStateChange;
__upload_http.upload.onprogress = __uploadProgress;
__upload_http.send(form_data);
};
var __clickAbortUploadingButton = function() {
__upload_http.onreadystatechange = null;
__upload_http.upload.onprogress = null;
__upload_http.abort();
__upload_http = null;
$("msd-progress").setAttribute("data-label", "Aborted");
$("msd-progress-value").style.width = "0%";
};
var __clickSwitchButton = function(to) {
var http = tools.makeRequest("POST", "/kvmd/msd/connect?to=" + to, function() {
if (http.readyState === 4) {
if (http.status !== 200) {
ui.error("Switch error:<br>", http.responseText);
}
}
__applyState();
});
__applyState();
$("msd-switch-to-" + to + "-button").disabled = true;
};
var __selectNewImageFile = function() {
var el_input = $("msd-select-new-image-file");
var image_file = (el_input.files.length ? el_input.files[0] : null);
if (image_file && image_file.size > __state.info.size) {
ui.error("New image is too big for your Mass Storage Device.<br>Maximum:", __formatSize(__state.info.size));
el_input.value = "";
image_file = null;
}
__image_file = image_file;
__applyState();
};
var __clickResetButton = function() {
var http = tools.makeRequest("POST", "/kvmd/msd/reset", function() {
if (http.readyState === 4) {
if (http.status !== 200) {
ui.error("MSD reset error:<br>", http.responseText);
}
}
__applyState();
});
__applyState();
};
var __applyState = function() {
if (__state.connected_to === "server") {
$("msd-another-another-user-uploads").style.display = "none";
$("msd-led").className = "led-green";
$("msd-status").innerHTML = $("msd-led").title = "Connected to Server";
$("msd-another-another-user-uploads").style.display = "none";
} else if (__state.busy) {
if (!__upload_http) {
$("msd-another-another-user-uploads").style.display = "block";
}
$("msd-led").className = "led-yellow-rotating-fast";
$("msd-status").innerHTML = $("msd-led").title = "Uploading new image";
} else {
$("msd-another-another-user-uploads").style.display = "none";
$("msd-led").className = "led-gray";
if (__state.in_operate) {
$("msd-status").innerHTML = $("msd-led").title = "Connected to KVM";
} else {
$("msd-status").innerHTML = $("msd-led").title = "Unavailable";
}
}
$("msd-not-in-operate").style.display = (__state.in_operate ? "none" : "block");
$("msd-current-image-broken").style.display = (
__state.in_operate && __state.info.image &&
!__state.info.image.complete && !__state.busy ? "block" : "none"
);
$("msd-current-image-name").innerHTML = (__state.in_operate && __state.info.image ? __state.info.image.name : "None");
$("msd-current-image-size").innerHTML = (__state.in_operate && __state.info.image ? __formatSize(__state.info.image.size) : "None");
$("msd-storage-size").innerHTML = (__state.in_operate ? __formatSize(__state.info.size) : "Unavailable");
$("msd-switch-to-kvm-button").disabled = (!__state.in_operate || __state.connected_to === "kvm" || __state.busy);
$("msd-switch-to-server-button").disabled = (!__state.in_operate || __state.connected_to === "server" || __state.busy);
$("msd-select-new-image-button").disabled = (!__state.in_operate || __state.connected_to !== "kvm" || __state.busy || __upload_http);
$("msd-upload-new-image-button").disabled = (!__state.in_operate || __state.connected_to !== "kvm" || __state.busy || !__image_file);
$("msd-abort-uploading-button").disabled = (!__state.in_operate || !__upload_http);
$("msd-reset-button").disabled = (!__state.in_operate || __upload_http);
$("msd-new-image").style.display = (__image_file ? "block" : "none");
$("msd-progress").setAttribute("data-label", "Waiting for upload ...");
$("msd-progress-value").style.width = "0%";
$("msd-new-image-name").innerHTML = (__image_file ? __image_file.name : "");
$("msd-new-image-size").innerHTML = (__image_file ? __formatSize(__image_file.size) : "");
};
var __formatSize = function(size) {
if (size > 0) {
var index = Math.floor( Math.log(size) / Math.log(1024) );
return (size / Math.pow(1024, index)).toFixed(2) * 1 + " " + ["B", "kB", "MB", "GB", "TB"][index];
} else {
return 0;
}
};
var __uploadStateChange = function() {
if (__upload_http.readyState === 4) {
if (__upload_http.status !== 200) {
ui.error("Can't upload image to the Mass Storage Device:<br>", __upload_http.responseText);
}
$("msd-select-new-image-file").value = "";
__image_file = null;
__upload_http = null;
__applyState();
}
};
var __uploadProgress = function(event) {
if(event.lengthComputable) {
var percent = Math.round((event.loaded * 100) / event.total);
$("msd-progress").setAttribute("data-label", percent + "%");
$("msd-progress-value").style.width = percent + "%";
}
};
__init__();
}

129
web/js/kvm/session.js Normal file
View File

@@ -0,0 +1,129 @@
function Session() {
// var self = this;
/********************************************************************************/
var __ws = null;
var __ping_timer = null;
var __missed_heartbeats = 0;
var __hid = new Hid();
var __atx = new Atx();
var __msd = new Msd();
var __streamer = new Streamer();
var __init__ = function() {
$("link-led").title = "Not connected yet...";
__loadKvmdVersion();
__startPoller();
};
/********************************************************************************/
var __loadKvmdVersion = function() {
var http = tools.makeRequest("GET", "/kvmd/info", function() {
if (http.readyState === 4) {
if (http.status === 200) {
var info = JSON.parse(http.responseText).result;
$("kvmd-version").innerHTML = "kvmd v" + info.version.kvmd;
$("about-version-kvmd").innerHTML = info.version.kvmd;
$("about-version-streamer").innerHTML = info.version.streamer + " (" + info.streamer + ")";
} else {
setTimeout(__loadKvmdVersion, 1000);
}
}
});
};
var __startPoller = function() {
$("link-led").className = "led-yellow";
$("link-led").title = "Connecting...";
var http = tools.makeRequest("GET", "/wsauth", function() {
if (http.readyState === 4) {
if (http.status === 200) {
__ws = new WebSocket((location.protocol === "https:" ? "wss" : "ws") + "://" + location.host + "/kvmd/ws");
__ws.onopen = __wsOpenHandler;
__ws.onmessage = __wsMessageHandler;
__ws.onerror = __wsErrorHandler;
__ws.onclose = __wsCloseHandler;
} else {
__wsCloseHandler(null);
}
}
});
};
var __wsOpenHandler = function(event) {
$("link-led").className = "led-green";
$("link-led").title = "Connected";
tools.debug("Session: socket opened:", event);
__streamer.loadInitialState();
__atx.loadInitialState();
__msd.loadInitialState();
__hid.setSocket(__ws);
__missed_heartbeats = 0;
__ping_timer = setInterval(__pingServer, 1000);
};
var __wsMessageHandler = function(event) {
// tools.debug("Session: received socket data:", event.data);
event = JSON.parse(event.data);
if (event.msg_type === "pong") {
__missed_heartbeats = 0;
} else if (event.msg_type === "event") {
if (event.msg.event === "streamer_state") {
__streamer.setState(event.msg.event_attrs);
} else if (event.msg.event === "atx_state") {
__atx.setState(event.msg.event_attrs);
} else if (event.msg.event === "msd_state") {
__msd.setState(event.msg.event_attrs);
}
}
};
var __wsErrorHandler = function(event) {
tools.error("Session: socket error:", event);
if (__ws) {
__ws.onclose = null;
__ws.close();
__wsCloseHandler(null);
}
};
var __wsCloseHandler = function(event) {
$("link-led").className = "led-gray";
tools.debug("Session: socket closed:", event);
if (__ping_timer) {
clearInterval(__ping_timer);
__ping_timer = null;
}
__streamer.clearState();
__atx.clearState();
__hid.setSocket(null);
__ws = null;
setTimeout(function() {
$("link-led").className = "led-yellow";
setTimeout(__startPoller, 500);
}, 500);
};
var __pingServer = function() {
try {
__missed_heartbeats += 1;
if (__missed_heartbeats >= 5) {
throw new Error("Too many missed heartbeats");
}
__ws.send(JSON.stringify({"event_type": "ping"}));
} catch (err) {
tools.error("Session: ping error:", err.message);
if (__ws) {
__ws.onclose = null;
__ws.close();
__wsCloseHandler(null);
}
}
};
__init__();
}

232
web/js/kvm/stream.js Normal file
View File

@@ -0,0 +1,232 @@
function Streamer() {
var self = this;
/********************************************************************************/
var __resolution = {width: 640, height: 480};
var __size_factor = 1;
var __client_key = tools.makeId();
var __client_id = "";
var __client_fps = -1;
var __prev = false;
var __init__ = function() {
$("stream-led").title = "Stream inactive";
$("stream-quality-slider").min = 5;
$("stream-quality-slider").max = 100;
$("stream-quality-slider").step = 5;
$("stream-quality-slider").value = 80;
tools.setOnUpSlider($("stream-quality-slider"), 1000, __updateQualityValue, (value) => __sendParam("quality", value));
$("stream-desired-fps-slider").min = 0;
$("stream-desired-fps-slider").max = 30;
$("stream-desired-fps-slider").step = 1;
$("stream-desired-fps-slider").value = 0;
tools.setOnUpSlider($("stream-desired-fps-slider"), 1000, __updateDesiredFpsValue, (value) => __sendParam("desired_fps", value));
$("stream-size-slider").min = 20;
$("stream-size-slider").max = 200;
$("stream-size-slider").step = 5;
$("stream-size-slider").value = 100;
$("stream-size-slider").oninput = () => __resize();
$("stream-size-slider").onchange = () => __resize();
tools.setOnClick($("stream-screenshot-button"), __clickScreenshotButton);
tools.setOnClick($("stream-reset-button"), __clickResetButton);
};
/********************************************************************************/
self.loadInitialState = function() {
var http = tools.makeRequest("GET", "/kvmd/streamer", function() {
if (http.readyState === 4) {
if (http.status === 200) {
self.setState(JSON.parse(http.responseText).result);
} else {
self.clearState();
setTimeout(self.loadInitialState, 1000);
}
}
});
};
self.setState = function(state) {
if (state.state) {
var source = state.state.source;
var stream = state.state.stream;
if (!__prev) {
$("stream-quality-slider").activated = false;
$("stream-desired-fps-slider").activated = false;
}
if (!$("stream-quality-slider").activated) {
$("stream-quality-slider").disabled = false;
if ($("stream-quality-slider").value !== source.quality) {
$("stream-quality-slider").value = source.quality;
__updateQualityValue(source.quality);
}
}
if (!$("stream-desired-fps-slider").activated) {
$("stream-desired-fps-slider").disabled = false;
if ($("stream-desired-fps-slider").value !== source.desired_fps) {
$("stream-desired-fps-slider").value = source.desired_fps;
__updateDesiredFpsValue(source.desired_fps);
}
}
if (__resolution.width !== source.resolution.width || __resolution.height !== source.resolution.height) {
__resolution = source.resolution;
if ($("stream-auto-resize-checkbox").checked) {
__adjustSizeFactor();
} else {
__applySizeFactor();
}
}
var stream_client = tools.getCookie("stream_client");
if (!__client_id && stream_client && stream_client.startsWith(__client_key + "/")) {
tools.info("Stream: found acceptable stream_client cookie:", stream_client);
__client_id = stream_client.slice(stream_client.indexOf("/") + 1);
}
if (stream.clients_stat.hasOwnProperty(__client_id)) {
__client_fps = stream.clients_stat[__client_id].fps;
} else {
__client_fps = -1;
}
if (!__prev) {
var path = "/streamer/stream?key=" + __client_key;
if (tools.browser.is_chrome || tools.browser.is_blink) {
// uStreamer fix for Blink https://bugs.chromium.org/p/chromium/issues/detail?id=527446
tools.info("Stream: using advance_headers=1 to fix Blink MJPG bugs");
path += "&advance_headers=1";
} else if (tools.browser.is_safari || tools.browser.is_ios) {
// uStreamer fix for WebKit
tools.info("Stream: using dual_final_frames=1 to fix WebKit MJPG bugs");
path += "&dual_final_frames=1";
}
$("stream-image").src = path;
$("stream-image").className = "stream-image-active";
$("stream-box").classList.remove("stream-box-inactive");
$("stream-led").className = "led-green";
$("stream-led").title = "Stream is active";
$("stream-screenshot-button").disabled = false;
$("stream-reset-button").disabled = false;
tools.info("Stream: acquired");
__prev = true;
}
__updateStreamHeader(true);
} else {
self.clearState();
}
};
self.clearState = function() {
tools.info("Stream: refreshing ...");
$("stream-image").className = "stream-image-inactive";
$("stream-box").classList.add("stream-box-inactive");
$("stream-led").className = "led-gray";
$("stream-led").title = "Stream inactive";
$("stream-screenshot-button").disabled = true;
$("stream-reset-button").disabled = true;
$("stream-quality-slider").disabled = true;
$("stream-desired-fps-slider").disabled = true;
__client_key = tools.makeId();
__client_id = "";
__client_fps = -1;
__prev = false;
__updateStreamHeader(false);
};
var __updateQualityValue = function(value) {
$("stream-quality-value").innerHTML = value + "%";
};
var __updateDesiredFpsValue = function(value) {
$("stream-desired-fps-value").innerHTML = (value == 0 ? "Unlimited" : value);
};
var __updateStreamHeader = function(online) {
var el_grab = document.querySelector("#stream-window-header .window-grab");
var el_info = $("stream-info");
if (online) {
var fps_suffix = (__client_fps >= 0 ? " / " + __client_fps + " fps" : "");
el_grab.innerHTML = el_info.innerHTML = "Stream &ndash; " + __resolution.width + "x" + __resolution.height + fps_suffix;
} else {
el_grab.innerHTML = el_info.innerHTML = "Stream &ndash; offline";
}
};
var __clickScreenshotButton = function() {
var el_a = document.createElement("a");
el_a.href = "/streamer/snapshot";
el_a.target = "_blank";
document.body.appendChild(el_a);
el_a.click();
setTimeout(() => document.body.removeChild(el_a), 0);
};
var __clickResetButton = function() {
var http = tools.makeRequest("POST", "/kvmd/streamer/reset", function() {
if (http.readyState === 4) {
if (http.status !== 200) {
ui.error("Can't reset stream:<br>", http.responseText);
}
}
});
};
var __sendParam = function(name, value) {
var http = tools.makeRequest("POST", "/kvmd/streamer/set_params?" + name + "=" + value, function() {
if (http.readyState === 4) {
if (http.status !== 200) {
ui.error("Can't configure stream:<br>", http.responseText);
}
}
});
};
var __resize = function(center=false) {
var size = $("stream-size-slider").value;
$("stream-size-value").innerHTML = size + "%";
__size_factor = size / 100;
__applySizeFactor(center);
};
var __adjustSizeFactor = function() {
var el_window = $("stream-window");
var el_slider = $("stream-size-slider");
var view = ui.getViewGeometry();
for (var size = 100; size >= el_slider.min; size -= el_slider.step) {
tools.info("Stream: adjusting size:", size);
$("stream-size-slider").value = size;
__resize(true);
var rect = el_window.getBoundingClientRect();
if (
rect.bottom <= view.bottom
&& rect.top >= view.top
&& rect.left >= view.left
&& rect.right <= view.right
) {
break;
}
}
};
var __applySizeFactor = function(center=false) {
var el_stream_image = $("stream-image");
el_stream_image.style.width = __resolution.width * __size_factor + "px";
el_stream_image.style.height = __resolution.height * __size_factor + "px";
ui.showWindow($("stream-window"), false, center);
};
__init__();
}