summaryrefslogtreecommitdiff
path: root/web/share/js
diff options
context:
space:
mode:
authorMaxim Devaev <[email protected]>2024-07-08 03:41:29 +0300
committerMaxim Devaev <[email protected]>2024-12-17 18:20:04 +0200
commit630610bc532299f15ff7ee12d40f617de450aae0 (patch)
treeca0a83f1aa5848a4605034c0394f1edfd0bea7ce /web/share/js
parente0bbf6968ef8295274793a564e717f95f42983d7 (diff)
switch
Diffstat (limited to 'web/share/js')
-rw-r--r--web/share/js/kvm/atx.js10
-rw-r--r--web/share/js/kvm/session.js20
-rw-r--r--web/share/js/kvm/switch.js606
-rw-r--r--web/share/js/tools.js6
4 files changed, 634 insertions, 8 deletions
diff --git a/web/share/js/kvm/atx.js b/web/share/js/kvm/atx.js
index 796a4eeb..0065e73c 100644
--- a/web/share/js/kvm/atx.js
+++ b/web/share/js/kvm/atx.js
@@ -32,6 +32,7 @@ export function Atx(__recorder) {
/************************************************************************/
+ var __has_switch = null; // Or true/false
var __state = null;
var __init__ = function() {
@@ -54,12 +55,12 @@ export function Atx(__recorder) {
}
if (state.enabled !== undefined) {
__state.enabled = state.enabled;
- tools.feature.setEnabled($("atx-dropdown"), __state.enabled);
+ tools.feature.setEnabled($("atx-dropdown"), (__state.enabled && !__has_switch));
}
if (__state.enabled !== undefined) {
if (state.busy !== undefined) {
+ __updateButtons(!state.busy);
__state.busy = state.busy;
- __updateButtons(!__state.busy);
}
if (state.leds !== undefined) {
__state.leds = state.leds;
@@ -75,6 +76,11 @@ export function Atx(__recorder) {
}
};
+ self.setHasSwitch = function(has_switch) {
+ __has_switch = has_switch;
+ self.setState(__state);
+ };
+
var __updateLeds = function(power, hdd, busy) {
$("atx-power-led").className = (busy ? "led-yellow" : (power ? "led-green" : "led-gray"));
$("atx-hdd-led").className = (hdd ? "led-red" : "led-gray");
diff --git a/web/share/js/kvm/session.js b/web/share/js/kvm/session.js
index 27b18b21..c2f13342 100644
--- a/web/share/js/kvm/session.js
+++ b/web/share/js/kvm/session.js
@@ -34,6 +34,7 @@ import {Msd} from "./msd.js";
import {Streamer} from "./stream.js";
import {Gpio} from "./gpio.js";
import {Ocr} from "./ocr.js";
+import {Switch} from "./switch.js";
export function Session() {
@@ -54,6 +55,7 @@ export function Session() {
var __msd = new Msd();
var __gpio = new Gpio(__recorder);
var __ocr = new Ocr(__streamer.getGeometry);
+ var __switch = new Switch();
var __info_hw_state = null;
var __info_fan_state = null;
@@ -368,9 +370,24 @@ export function Session() {
case "hid_state": __hid.setState(data.event); break;
case "hid_keymaps_state": __paste.setState(data.event); break;
case "atx_state": __atx.setState(data.event); break;
- case "msd_state": __msd.setState(data.event); break;
case "streamer_state": __streamer.setState(data.event); break;
case "ocr_state": __ocr.setState(data.event); break;
+
+ case "msd_state":
+ if (data.event.online === false) {
+ __switch.setMsdConnected(false);
+ } else if (data.event.drive !== undefined) {
+ __switch.setMsdConnected(data.event.drive.connected);
+ }
+ __msd.setState(data.event);
+ break;
+
+ case "switch_state":
+ if (data.event.model) {
+ __atx.setHasSwitch(data.event.model.ports.length > 0);
+ }
+ __switch.setState(data.event);
+ break;
}
};
@@ -401,6 +418,7 @@ export function Session() {
__streamer.setState(null);
__ocr.setState(null);
__recorder.setSocket(null);
+ __switch.setState(null);
__ws = null;
setTimeout(function() {
diff --git a/web/share/js/kvm/switch.js b/web/share/js/kvm/switch.js
new file mode 100644
index 00000000..112d8f15
--- /dev/null
+++ b/web/share/js/kvm/switch.js
@@ -0,0 +1,606 @@
+/*****************************************************************************
+# #
+# KVMD - The main PiKVM daemon. #
+# #
+# Copyright (C) 2018-2024 Maxim Devaev <[email protected]> #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <https://www.gnu.org/licenses/>. #
+# #
+*****************************************************************************/
+
+
+"use strict";
+
+
+import {tools, $} from "../tools.js";
+import {wm} from "../wm.js";
+
+
+export function Switch() {
+ var self = this;
+
+ /************************************************************************/
+
+ var __state = null;
+ var __msd_connected = false;
+
+ var __init__ = function() {
+ tools.selector.addOption($("switch-edid-selector"), "Default", "default");
+ $("switch-edid-selector").onchange = __selectEdid;
+
+ tools.el.setOnClick($("switch-edid-add-button"), __clickAddEdidButton);
+ tools.el.setOnClick($("switch-edid-remove-button"), __clickRemoveEdidButton);
+ tools.el.setOnClick($("switch-edid-copy-data-button"), __clickCopyEdidDataButton);
+
+ tools.storage.bindSimpleSwitch($("switch-atx-ask-switch"), "switch.atx.ask", true);
+
+ for (let role of ["inactive", "active", "flashing", "beacon", "bootloader"]) {
+ let el_brightness = $(`switch-color-${role}-brightness-slider`);
+ tools.slider.setParams(el_brightness, 0, 255, 1, 0);
+ el_brightness.onchange = $(`switch-color-${role}-input`).onchange = tools.partial(__selectColor, role);
+ tools.el.setOnClick($(`switch-color-${role}-default-button`), tools.partial(__clickSetDefaultColorButton, role));
+ }
+ };
+
+ /************************************************************************/
+
+ self.setMsdConnected = function(connected) {
+ __msd_connected = connected;
+ };
+
+ self.setState = function(state) {
+ if (state) {
+ if (!__state) {
+ __state = {};
+ }
+ if (state.model) {
+ __state = {};
+ __applyModel(state.model);
+ }
+ if (__state.model) {
+ if (state.summary) {
+ __applySummary(state.summary);
+ }
+ if (state.beacons) {
+ __applyBeacons(state.beacons);
+ }
+ if (state.usb) {
+ __applyUsb(state.usb);
+ }
+ if (state.video) {
+ __applyVideo(state.video);
+ }
+ if (state.atx) {
+ __applyAtx(state.atx);
+ }
+ if (state.edids) {
+ __applyEdids(state.edids);
+ }
+ if (state.colors) {
+ __applyColors(state.colors);
+ }
+ }
+ } else {
+ tools.feature.setEnabled($("switch-dropdown"), false);
+ $("switch-chain").innerText = "";
+ $("switch-active-port").innerText = "N/A";
+ __setPowerLedState($("switch-atx-power-led"), false, false);
+ __setLedState($("switch-atx-hdd-led"), "red", false);
+ __state = null;
+ }
+ };
+
+ var __applyColors = function(colors) {
+ for (let role in colors) {
+ let color = colors[role];
+ $(`switch-color-${role}-input`).value = (
+ "#"
+ + color.red.toString(16).padStart(2, "0")
+ + color.green.toString(16).padStart(2, "0")
+ + color.blue.toString(16).padStart(2, "0")
+ );
+ $(`switch-color-${role}-brightness-slider`).value = color.brightness;
+ }
+ __state.colors = colors;
+ };
+
+ var __selectColor = function(role) {
+ let el_color = $(`switch-color-${role}-input`);
+ let el_brightness = $(`switch-color-${role}-brightness-slider`);
+ let color = __state.colors[role];
+ let brightness = parseInt(el_brightness.value);
+ let rgbx = (
+ el_color.value.slice(1)
+ + ":" + brightness.toString(16).padStart(2, "0")
+ + ":" + color.blink_ms.toString(16).padStart(4, "0")
+ );
+ __sendPost("/api/switch/set_colors", {[role]: rgbx}, function() {
+ el_color.value = (
+ "#"
+ + color.red.toString(16).padStart(2, "0")
+ + color.green.toString(16).padStart(2, "0")
+ + color.blue.toString(16).padStart(2, "0")
+ );
+ el_brightness.value = color.brightness;
+ });
+ };
+
+ var __clickSetDefaultColorButton = function(role) {
+ __sendPost("/api/switch/set_colors", {[role]: "default"});
+ };
+
+ var __applyEdids = function(edids) {
+ let el = $("switch-edid-selector");
+ let old_edid_id = el.value;
+ el.options.length = 1;
+ for (let kv of Object.entries(edids.all)) {
+ if (kv[0] !== "default") {
+ tools.selector.addOption(el, kv[1].name, kv[0]);
+ }
+ }
+ el.value = (old_edid_id in edids.all ? old_edid_id : "default");
+
+ for (let port in __state.model.ports) {
+ let custom = (edids.used[port] !== "default");
+ $(`__switch-custom-edid-p${port}`).style.visibility = (custom ? "unset" : "hidden");
+ }
+
+ __state.edids = edids;
+ __selectEdid();
+ };
+
+ var __selectEdid = function() {
+ let edid_id = $("switch-edid-selector").value;
+ let edid = null;
+ try { edid = __state.edids.all[edid_id]; } catch { edid_id = ""; }
+ let parsed = (edid ? edid.parsed : null);
+ let na = "<i>&lt;Not Available&gt;</i>";
+ $("switch-edid-info-mfc-id").innerHTML = (parsed ? tools.escape(parsed.mfc_id) : na);
+ $("switch-edid-info-product-id").innerHTML = (parsed ? tools.escape(`0x${parsed.product_id.toString(16).toUpperCase()}`) : na);
+ $("switch-edid-info-serial").innerHTML = (parsed ? tools.escape(`0x${parsed.serial.toString(16).toUpperCase()}`) : na);
+ $("switch-edid-info-monitor-name").innerHTML = ((parsed && parsed.monitor_name) ? tools.escape(parsed.monitor_name) : na);
+ $("switch-edid-info-monitor-serial").innerHTML = ((parsed && parsed.monitor_serial) ? tools.escape(parsed.monitor_serial) : na);
+ $("switch-edid-info-audio").innerHTML = (parsed ? (parsed.audio ? "Yes" : "No") : na);
+ tools.el.setEnabled($("switch-edid-remove-button"), (edid_id && (edid_id !== "default")));
+ tools.el.setEnabled($("switch-edid-copy-data-button"), !!edid_id);
+ };
+
+ var __clickAddEdidButton = function() {
+ let create_content = function(el_parent, el_ok_button) {
+ tools.el.setEnabled(el_ok_button, false);
+ el_parent.innerHTML = `
+ <table>
+ <tr>
+ <td>Name:</td>
+ <td><input
+ type="text" autocomplete="off" id="__switch-edid-new-name-input"
+ placeholder="Enter some meaningful name"
+ style="width:100%"
+ /></td>
+ </tr>
+ <tr><td colspan="2">HEX data:</td></tr>
+ <tr>
+ <td colspan="2"><textarea
+ id="__switch-edid-new-data-text" placeholder="Like 0123ABCD..."
+ style="min-width:350px"
+ ></textarea><td>
+ </table>
+ `;
+ let el_name = $("__switch-edid-new-name-input");
+ let el_data = $("__switch-edid-new-data-text");
+ 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)));
+ };
+ };
+
+ wm.modal("Add new EDID", create_content, true, true).then(function(ok) {
+ if (ok) {
+ let name = $("__switch-edid-new-name-input").value;
+ let data = $("__switch-edid-new-data-text").value;
+ __sendPost("/api/switch/edids/create", {"name": name, "data": data});
+ }
+ });
+ };
+
+ var __clickRemoveEdidButton = function() {
+ let edid_id = $("switch-edid-selector").value;
+ if (edid_id && __state && __state.edids) {
+ let name = __state.edids.all[edid_id].name;
+ let html = "Are you sure to remove this EDID?<br>Ports that used it will change it to the default.";
+ wm.confirm(html, name).then(function(ok) {
+ if (ok) {
+ __sendPost("/api/switch/edids/remove", {"id": edid_id});
+ }
+ });
+ }
+ };
+
+ var __clickCopyEdidDataButton = function() {
+ let edid_id = $("switch-edid-selector").value;
+ if (edid_id && __state && __state.edids) {
+ let data = __state.edids.all[edid_id].data;
+ data = data.replace(/(.{32})/g, "$1\n");
+ wm.copyTextToClipboard(data);
+ }
+ };
+
+ var __applyUsb = function(usb) {
+ for (let port = 0; port < __state.model.ports.length; ++port) {
+ if (!__state.usb || __state.usb.links[port] !== usb.links[port]) {
+ __setLedState($(`__switch-usb-led-p${port}`), "green", usb.links[port]);
+ }
+ }
+ __state.usb = usb;
+ };
+
+ var __applyVideo = function(video) {
+ for (let port = 0; port < __state.model.ports.length; ++port) {
+ if (!__state.video || __state.video.links[port] !== video.links[port]) {
+ __setLedState($(`__switch-video-led-p${port}`), "green", video.links[port]);
+ }
+ }
+ __state.video = video;
+ };
+
+ var __applyAtx = function(atx) {
+ for (let port = 0; port < __state.model.ports.length; ++port) {
+ let busy = atx.busy[port];
+ if (!__state.atx || __state.atx.leds.power[port] !== atx.leds.power[port] || __state.atx.busy[port] !== busy) {
+ let power = atx.leds.power[port];
+ __setPowerLedState($(`__switch-atx-power-led-p${port}`), power, busy);
+ if (port === __state.summary.active_port) {
+ // summary есть всегда, если есть model, и atx обновляется последним в setState()
+ __setPowerLedState($("switch-atx-power-led"), power, busy);
+ }
+ }
+ if (!__state.atx || __state.atx.leds.hdd[port] !== atx.leds.hdd[port]) {
+ let hdd = atx.leds.hdd[port];
+ __setLedState($(`__switch-atx-hdd-led-p${port}`), "red", hdd);
+ if (port === __state.summary.active_port) {
+ __setLedState($("switch-atx-hdd-led"), "red", hdd);
+ }
+ }
+ if (!__state.atx || __state.atx.busy[port] !== busy) {
+ tools.el.setEnabled($(`__switch-atx-power-button-p${port}`), !busy);
+ tools.el.setEnabled($(`__switch-atx-power-long-button-p${port}`), !busy);
+ tools.el.setEnabled($(`__switch-atx-reset-button-p${port}`), !busy);
+ }
+ }
+ __state.atx = atx;
+ };
+
+ var __applyBeacons = function(beacons) {
+ for (let unit = 0; unit < __state.model.units.length; ++unit) {
+ if (!__state.beacons || __state.beacons.uplinks[unit] !== beacons.uplinks[unit]) {
+ __setLedState($(`__switch-beacon-led-u${unit}`), "green", beacons.uplinks[unit]);
+ }
+ if (!__state.beacons || __state.beacons.downlinks[unit] !== beacons.downlinks[unit]) {
+ __setLedState($(`__switch-beacon-led-d${unit}`), "green", beacons.downlinks[unit]);
+ }
+ }
+ for (let port = 0; port < __state.model.ports.length; ++port) {
+ if (!__state.beacons || __state.beacons.ports[port] !== beacons.ports[port]) {
+ __setLedState($(`__switch-beacon-led-p${port}`), "green", beacons.ports[port]);
+ }
+ }
+ __state.beacons = beacons;
+ };
+
+ var __applySummary = function(summary) {
+ let active = summary.active_port;
+ if (!__state.summary || __state.summary.active_port !== active) {
+ if (active < 0 || active >= __state.model.ports.length) {
+ $("switch-active-port").innerText = "N/A";
+ } else {
+ $("switch-active-port").innerText = "p" + __formatPort(__state.model, active);
+ }
+ for (let port = 0; port < __state.model.ports.length; ++port) {
+ __setLedState($(`__switch-port-led-p${port}`), "green", (port === active));
+ }
+ }
+ if (__state.atx) {
+ // Синхронизация светодиодов ATX при смене порта
+ let power = false;
+ let busy = false;
+ let hdd = false;
+ if (active >= 0 && active < __state.model.ports.length) {
+ power = __state.atx.leds.power[active];
+ hdd = __state.atx.leds.hdd[active];
+ busy = __state.atx.busy[active];
+ }
+ __setPowerLedState($("switch-atx-power-led"), power, busy);
+ __setLedState($("switch-atx-hdd-led"), "red", hdd);
+ }
+ __state.summary = summary;
+ };
+
+ var __applyModel = function(model) {
+ tools.feature.setEnabled($("switch-dropdown"), model.ports.length);
+
+ let content = "";
+ let unit = -1;
+ for (let port = 0; port < model.ports.length; ++port) {
+ let pa = model.ports[port]; // pa == port attrs
+ if (unit !== pa.unit) {
+ unit = pa.unit;
+ content += `${unit > 0 ? "<tr><td colspan=100><hr></td></tr>" : ""}
+ <tr>
+ <td></td><td></td><td></td>
+ <td class="value">Unit: ${unit + 1}</td>
+ <td></td>
+ <td colspan=100>
+ <div class="buttons-row">
+ <button id="__switch-beacon-button-u${unit}" class="small" title="Toggle uplink Beacon Led">
+ <img id="__switch-beacon-led-u${unit}" class="inline-lamp led-gray" src="/share/svg/led-beacon.svg"/>
+ Uplink
+ </button>
+ <button id="__switch-beacon-button-d${unit}" class="small" title="Toggle downlink Beacon Led">
+ <img id="__switch-beacon-led-d${unit}" class="inline-lamp led-gray" src="/share/svg/led-beacon.svg"/>
+ Downlink
+ </button>
+ </div>
+ </td>
+ </tr>
+ <tr><td colspan=100><hr></td></tr>
+ `;
+ }
+ content += `
+ <tr>
+ <td>Port:</td>
+ <td class="value">${__formatPort(model, port)}</td>
+ <td>&nbsp;&nbsp;</td>
+ <td>
+ <div class="buttons-row">
+ <button id="__switch-port-button-p${port}" title="Activate this port">
+ <img id="__switch-port-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-circle.svg"/>
+ </button>
+ <button id="__switch-params-button-p${port}" title="Configure this port">
+ <img id="__switch-params-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-gear.svg"/>
+ </button>
+ </div>
+ </td>
+ <td>
+ <span
+ id="__switch-custom-edid-p${port}" style="visibility:hidden"
+ title="A non-default EDID is used on this port"
+ >
+ &#9913;
+ </span>
+ &nbsp;&nbsp;&nbsp;&nbsp;
+ ${pa.name.length > 0 ? tools.escape(pa.name) : ("Host " + (port + 1))}
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ </td>
+ <td style="font-size:1em">
+ <button id="__switch-beacon-button-p${port}" class="small" title="Toggle Beacon Led on this port">
+ <img id="__switch-beacon-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-beacon.svg"/>
+ </button>
+ </td>
+ <td>
+ <img id="__switch-video-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-video.svg" title="Video Link"/>
+ <img id="__switch-usb-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-usb.svg" title="USB Link"/>
+ <img id="__switch-atx-power-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-atx-power.svg" title="Power Led"/>
+ <img id="__switch-atx-hdd-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-atx-hdd.svg" title="HDD Led"/>
+ </td>
+ <td>
+ <div class="buttons-row">
+ <button id="__switch-atx-power-button-p${port}" class="small">Power <sup><i>short</i></sup></button>
+ <button id="__switch-atx-power-long-button-p${port}" class="small"><sup><i>long</i></sup></button>
+ <button id="__switch-atx-reset-button-p${port}" class="small">Reset</button>
+ </div>
+ </td>
+ </tr>
+ `;
+ }
+ $("switch-chain").innerHTML = content;
+
+ for (let unit = 0; unit < model.units.length; ++unit) {
+ tools.el.setOnClick($(`__switch-beacon-button-u${unit}`), tools.partial(__switchUplinkBeacon, unit));
+ tools.el.setOnClick($(`__switch-beacon-button-d${unit}`), tools.partial(__switchDownlinkBeacon, unit));
+ }
+
+ for (let port = 0; port < model.ports.length; ++port) {
+ tools.el.setOnClick($(`__switch-port-button-p${port}`), tools.partial(__switchActivePort, port));
+ tools.el.setOnClick($(`__switch-params-button-p${port}`), tools.partial(__showParamsDialog, port));
+ tools.el.setOnClick($(`__switch-beacon-button-p${port}`), tools.partial(__switchPortBeacon, port));
+ tools.el.setOnClick($(`__switch-atx-power-button-p${port}`), tools.partial(__atxClick, port, "power"));
+ tools.el.setOnClick($(`__switch-atx-power-long-button-p${port}`), tools.partial(__atxClick, port, "power_long"));
+ tools.el.setOnClick($(`__switch-atx-reset-button-p${port}`), tools.partial(__atxClick, port, "reset"));
+ }
+
+ __setPowerLedState($("switch-atx-power-led"), false, false);
+ __setLedState($("switch-atx-hdd-led"), "red", false);
+
+ __state.model = model;
+ };
+
+ var __showParamsDialog = function(port) {
+ if (!__state || !__state.model || !__state.edids) {
+ return;
+ }
+
+ let model = __state.model;
+ let edids = __state.edids;
+
+ let atx_actions = {
+ "power": "ATX power click",
+ "power_long": "Power long",
+ "reset": "Reset click",
+ };
+
+ let add_edid_option = function(el, attrs, id) {
+ tools.selector.addOption(el, attrs.name, id, (edids.used[port] === id));
+ if (attrs.parsed !== null) {
+ let parsed = attrs.parsed;
+ let text = "\xA0\xA0\xA0\xA0\xA0\u2570 ";
+ text += (parsed.monitor_name !== null ? parsed.monitor_name : parsed.mfc_id);
+ text += (parsed.audio ? "; +Audio" : "; -Audio");
+ tools.selector.addComment(el, text);
+ }
+ };
+
+ let create_content = function(el_parent) {
+ let html = `
+ <table>
+ <tr>
+ <td>Port name:</td>
+ <td><input
+ type="text" autocomplete="off" id="__switch-port-name-input"
+ value="${tools.escape(model.ports[port].name)}" placeholder="Host ${port + 1}"
+ style="width:100%"
+ /></td>
+ </tr>
+ <tr>
+ <td>EDID:</td>
+ <td><select id="__switch-port-edid-selector" style="width: 100%"></select></td>
+ </tr>
+ </table>
+ <hr>
+ <table>
+ `;
+ for (let kv of Object.entries(atx_actions)) {
+ html += `
+ <tr>
+ <td style="white-space: nowrap">${tools.escape(kv[1])}:</td>
+ <td style="width: 100%"><input type="range" id="__switch-port-atx-click-${kv[0]}-delay-slider"/></td>
+ <td id="__switch-port-atx-click-${kv[0]}-delay-value"></td>
+ <td>&nbsp;&nbsp;&nbsp;</td>
+ <td><button
+ id="__switch-port-atx-click-${kv[0]}-delay-default-button"
+ class="small" title="Reset default"
+ >&#8635;</button></td>
+ </tr>
+ `;
+ }
+ html += "</table>";
+ el_parent.innerHTML = html;
+
+ let el_selector = $("__switch-port-edid-selector");
+ add_edid_option(el_selector, edids.all["default"], "default");
+ for (let kv of Object.entries(edids.all)) {
+ if (kv[0] !== "default") {
+ tools.selector.addSeparator(el_selector, 20);
+ add_edid_option(el_selector, kv[1], kv[0]);
+ }
+ }
+
+ for (let action of Object.keys(atx_actions)) {
+ let limits = model.limits.atx.click_delays[action];
+ let el_slider = $(`__switch-port-atx-click-${action}-delay-slider`);
+ let display_value = tools.partial(function(action, value) {
+ $(`__switch-port-atx-click-${action}-delay-value`).innerText = `${value.toFixed(1)}`;
+ }, action);
+ let reset_default = tools.partial(function(el_slider, limits) {
+ tools.slider.setValue(el_slider, limits["default"]);
+ }, el_slider, limits);
+ tools.slider.setParams(el_slider, limits.min, limits.max, 0.5, model.ports[port].atx.click_delays[action], display_value);
+ tools.el.setOnClick($(`__switch-port-atx-click-${action}-delay-default-button`), reset_default);
+ }
+ };
+
+ wm.modal(`Port ${__formatPort(__state.model, port)} settings`, create_content, true, true).then(function(ok) {
+ if (ok) {
+ let params = {
+ "port": port,
+ "edid_id": $("__switch-port-edid-selector").value,
+ "name": $("__switch-port-name-input").value,
+ };
+ for (let action of Object.keys(atx_actions)) {
+ params[`atx_click_${action}_delay`] = tools.slider.getValue($(`__switch-port-atx-click-${action}-delay-slider`));
+ };
+ __sendPost("/api/switch/set_port_params", params);
+ }
+ });
+ };
+
+ var __formatPort = function(model, port) {
+ if (model.units.length > 1) {
+ return `${model.ports[port].unit + 1}.${model.ports[port].channel + 1}`;
+ } else {
+ return `${port + 1}`;
+ }
+ };
+
+ var __setLedState = function(el, color, on) {
+ el.classList.toggle(`led-${color}`, on);
+ el.classList.toggle("led-gray", !on);
+ };
+
+ var __setPowerLedState = function(el, power, busy) {
+ el.classList.toggle("led-green", (power && !busy));
+ el.classList.toggle("led-yellow", busy);
+ el.classList.toggle("led-gray", !(power || busy));
+ };
+
+ var __switchActivePort = function(port) {
+ if (__msd_connected) {
+ wm.error(`
+ Oops! Before port switching, please disconnect an active Mass Storage Drive image first.
+ Otherwise, it will break a current USB operation (OS installation, Live CD, or whatever).
+ `);
+ } else {
+ __sendPost("/api/switch/set_active", {"port": port});
+ }
+ };
+
+ var __switchUplinkBeacon = function(unit) {
+ let state = false;
+ try { state = !__state.beacons.uplinks[unit]; } catch {}; // eslint-disable-line no-empty
+ __sendPost("/api/switch/set_beacon", {"uplink": unit, "state": state});
+ };
+
+ var __switchDownlinkBeacon = function(unit) {
+ let state = false;
+ try { state = !__state.beacons.downlinks[unit]; } catch {}; // eslint-disable-line no-empty
+ __sendPost("/api/switch/set_beacon", {"downlink": unit, "state": state});
+ };
+
+ var __switchPortBeacon = function(port) {
+ let state = false;
+ try { state = !__state.beacons.ports[port]; } catch {}; // eslint-disable-line no-empty
+ __sendPost("/api/switch/set_beacon", {"port": port, "state": state});
+ };
+
+ var __atxClick = function(port, button) {
+ let click_button = function() {
+ __sendPost("/api/switch/atx/click", {"port": port, "button": button});
+ };
+ if ($("switch-atx-ask-switch").checked) {
+ wm.confirm(`
+ Are you sure you want to press the <b>${button}</b> button?<br>
+ Warning! This could case data loss on the server.
+ `).then(function(ok) {
+ if (ok) {
+ click_button();
+ }
+ });
+ } else {
+ click_button();
+ }
+ };
+
+ var __sendPost = function(url, params, error_callback=null) {
+ tools.httpPost(url, params, function(http) {
+ if (http.status !== 200) {
+ if (error_callback) {
+ error_callback();
+ }
+ wm.error("Switch error", http.responseText);
+ }
+ });
+ };
+
+ __init__();
+}
diff --git a/web/share/js/tools.js b/web/share/js/tools.js
index 046813c6..f5ddae8b 100644
--- a/web/share/js/tools.js
+++ b/web/share/js/tools.js
@@ -78,7 +78,7 @@ export var tools = new function() {
};
self.partial = function(func, ...args) {
- return () => func(...args);
+ return (...rest) => func(...args, ...rest);
};
self.upperFirst = function(text) {
@@ -104,10 +104,6 @@ export var tools = new function() {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
- self.formatHex = function(value) {
- return `0x${value.toString(16).toUpperCase()}`;
- };
-
self.formatSize = function(size) {
if (size > 0) {
let index = Math.floor( Math.log(size) / Math.log(1024) );