mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-31 18:11:54 +08:00
* Rework of #39 patch from @arykov (thanks) * styled resize corner * fixed squashing of resizaable window on moving * keep window size and opsition after full screen * not necessary * attempt to fix desktop overflow * improved * ios fixes * refactoring
This commit is contained in:
2464
web/kvm/index.html
2464
web/kvm/index.html
File diff suppressed because it is too large
Load Diff
@@ -22,12 +22,6 @@ li(class="right")
|
|||||||
td Max FPS:
|
td Max FPS:
|
||||||
td #[input(disabled type="range" id="stream-desired-fps-slider" class="slider")]
|
td #[input(disabled type="range" id="stream-desired-fps-slider" class="slider")]
|
||||||
td(id="stream-desired-fps-value" class="value") 0
|
td(id="stream-desired-fps-value" class="value") 0
|
||||||
tr
|
|
||||||
td Stream size:
|
|
||||||
td #[input(type="range" id="stream-size-slider" class="slider")]
|
|
||||||
td(id="stream-size-value" class="value" style="width: 4em") 100%
|
|
||||||
hr
|
|
||||||
+menu_switch("stream-auto-resize-switch", "Auto-resize stream window", true, true)
|
|
||||||
hr
|
hr
|
||||||
div(class="buttons buttons-row")
|
div(class="buttons buttons-row")
|
||||||
button(data-force-hide-menu id="show-stream-button" class="row33") • Show stream
|
button(data-force-hide-menu id="show-stream-button" class="row33") • Show stream
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ mixin about_tab(name, title, checked=false)
|
|||||||
else
|
else
|
||||||
span(class="code-comment") No data
|
span(class="code-comment") No data
|
||||||
|
|
||||||
+window("about-window", "About", false)
|
div(id="about-window" class="window")
|
||||||
|
div(class="window-header")
|
||||||
|
div(class="window-grab") About
|
||||||
|
button(class="window-button-close") ×
|
||||||
|
|
||||||
div(id="about")
|
div(id="about")
|
||||||
table
|
table
|
||||||
tr
|
tr
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ mixin empty_key(width=0)
|
|||||||
mixin lamp(cls)
|
mixin lamp(cls)
|
||||||
img(class=`inline-lamp ${cls} led-gray` src=`${svg_dir}/led-square.svg`)
|
img(class=`inline-lamp ${cls} led-gray` src=`${svg_dir}/led-square.svg`)
|
||||||
|
|
||||||
+window("keyboard-window", "Virtual Keyboard", true)
|
div(id="keyboard-window" class="window")
|
||||||
|
div(id="keyboard-window-header" class="window-header")
|
||||||
|
div(class="window-grab") Virtual Keyboard
|
||||||
|
button(class="window-button-close") ×
|
||||||
|
|
||||||
div(id="keyboard-desktop" class="keypad" align="center")
|
div(id="keyboard-desktop" class="keypad" align="center")
|
||||||
div(class="keypad-block")
|
div(class="keypad-block")
|
||||||
div(class="keypad-row")
|
div(class="keypad-row")
|
||||||
|
|||||||
@@ -1,10 +1,25 @@
|
|||||||
- let confirm_msg = "Do you want to close the stream? This action will temporarily stop<br>";
|
- let confirm_msg = "Do you want to close the stream? This action will temporarily stop<br>";
|
||||||
- confirm_msg += "the video transmission until you open the broadcast again.<br>";
|
- confirm_msg += "the video transmission until you open the broadcast again.<br>";
|
||||||
- confirm_msg += "This can be useful for saving traffic.";
|
- confirm_msg += "This can be useful for saving traffic.";
|
||||||
+window("stream-window", "Stream", true, confirm_msg)
|
|
||||||
|
div(id="stream-window" class="window window-resizable" data-close-confirm=confirm_msg)
|
||||||
|
div(id="stream-window-header" class="window-header")
|
||||||
|
div(class="window-grab") Stream
|
||||||
|
button(class="window-button-close") ×
|
||||||
|
button(class="window-button-maximize") ☐
|
||||||
|
button(class="window-button-full-screen") ⤢
|
||||||
|
|
||||||
div(id="stream-info")
|
div(id="stream-info")
|
||||||
|
|
||||||
div(id="stream-box" class="stream-box-inactive")
|
div(id="stream-box" class="stream-box-inactive")
|
||||||
|
div(class="window-lock-alert hidden")
|
||||||
|
| #[b Failed to acquire keyboard lock.#[br]Shortcuts like Alt+Tab, Ctrl+W, Ctrl+N might not be captured.]#[br]
|
||||||
|
| #[br]
|
||||||
|
| For best keyboard handling use a #[a(href="https://developer.mozilla.org/en-US/docs/Web/API/Keyboard_API#Browser_compatibility") browser with keyboard lock support API].#[br]
|
||||||
|
| In Chrome use HTTPS and enable #[i system-keyboard-lock] by putting at URL #[i chrome://flags/#system-keyboard-lock].#[br]
|
||||||
|
| More details on keyboard lock API are #[a(href="https://wicg.github.io/keyboard-lock/" target="_blank") here].
|
||||||
img(id="stream-image" class="stream-image-inactive" src=`${png_dir}/blank-stream.png`)
|
img(id="stream-image" class="stream-image-inactive" src=`${png_dir}/blank-stream.png`)
|
||||||
|
|
||||||
div(id="stream-mouse-buttons" class="keypad" align="center")
|
div(id="stream-mouse-buttons" class="keypad" align="center")
|
||||||
div(class="keypad-block")
|
div(class="keypad-block")
|
||||||
div(class="keypad-row")
|
div(class="keypad-row")
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
mixin window(id, title, with_header_id=false, close_confirm_msg="")
|
|
||||||
div(id=id class="window" data-close-confirm=close_confirm_msg)
|
|
||||||
div(id=(with_header_id ? `${id}-header` : "") class="window-header")
|
|
||||||
div(class="window-grab") #{title}
|
|
||||||
button(class="window-button-close") ×
|
|
||||||
block
|
|
||||||
|
|
||||||
include window-stream.pug
|
include window-stream.pug
|
||||||
include window-keyboard.pug
|
include window-keyboard.pug
|
||||||
include window-about.pug
|
include window-about.pug
|
||||||
|
|||||||
@@ -20,11 +20,19 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
div#stream-window {
|
||||||
|
min-width: 400px;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
div#stream-info {
|
div#stream-info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#stream-box {
|
div#stream-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: var(--border-window-thin);
|
border: var(--border-window-thin);
|
||||||
@@ -45,8 +53,9 @@ div.stream-box-mouse-enabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
img#stream-image {
|
img#stream-image {
|
||||||
width: 640px;
|
width: 100%;
|
||||||
height: 480px;
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
display: block;
|
display: block;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
@@ -69,9 +78,12 @@ div#stream-mouse-buttons {
|
|||||||
div#stream-window {
|
div#stream-window {
|
||||||
padding-top: 3px !important;
|
padding-top: 3px !important;
|
||||||
border-top: 0 !important;
|
border-top: 0 !important;
|
||||||
border-radius: 0 0 8px 8px !important;
|
border-left: 0 !important;
|
||||||
|
border-right: 0 !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
top: 50px !important;
|
top: 50px !important;
|
||||||
left: 50% !important;
|
left: 50% !important;
|
||||||
|
width: 100% !important;
|
||||||
-webkit-transform: translateX(-50%) !important;
|
-webkit-transform: translateX(-50%) !important;
|
||||||
transform: translateX(-50%) !important;
|
transform: translateX(-50%) !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,8 @@
|
|||||||
--cs-key-pressed-fg: #6c7481;
|
--cs-key-pressed-fg: #6c7481;
|
||||||
--cs-key-holded-bg: #436a8a;
|
--cs-key-holded-bg: #436a8a;
|
||||||
|
|
||||||
|
--cs-corner-bg: #5b90bb;
|
||||||
|
|
||||||
--shadow-micro: 1px 2px 4px 0 rgba(0, 0, 0, 0.4);
|
--shadow-micro: 1px 2px 4px 0 rgba(0, 0, 0, 0.4);
|
||||||
--shadow-small: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
|
--shadow-small: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
|
||||||
--shadow-big: 0 8px 16px 0 rgba(0, 0, 0, 0.4);
|
--shadow-big: 0 8px 16px 0 rgba(0, 0, 0, 0.4);
|
||||||
|
|||||||
@@ -34,9 +34,31 @@ div.window {
|
|||||||
background-color: var(--cs-window-default-bg);
|
background-color: var(--cs-window-default-bg);
|
||||||
padding: 30px 9px 9px 9px;
|
padding: 30px 9px 9px 9px;
|
||||||
}
|
}
|
||||||
|
div.window-resizable {
|
||||||
|
resize: both;
|
||||||
|
}
|
||||||
div.window-active {
|
div.window-active {
|
||||||
border: var(--border-intensive-2px) !important;
|
border: var(--border-intensive-2px) !important;
|
||||||
}
|
}
|
||||||
|
div.window-resizable.window-active::-webkit-resizer {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0 0 20px 20px;
|
||||||
|
border-color: transparent transparent var(--cs-corner-bg) transparent;
|
||||||
|
/* border-bottom-right-radius: 8px; */
|
||||||
|
/* Size does not work */
|
||||||
|
display:block;
|
||||||
|
width: 20px !important;
|
||||||
|
height: 20px !important;
|
||||||
|
}
|
||||||
|
div.window-full-screen {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
div.window div.window-header {
|
div.window div.window-header {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -68,17 +90,43 @@ div.window div.window-header-grabbed {
|
|||||||
border-bottom: var(--border-intensive-thin);
|
border-bottom: var(--border-intensive-thin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.window div.window-header button.window-button-full-screen,
|
||||||
|
div.window div.window-header button.window-button-maximize,
|
||||||
div.window div.window-header button.window-button-close {
|
div.window div.window-header button.window-button-close {
|
||||||
border: none;
|
border: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -2px;
|
top: -2px;
|
||||||
right: -6px;
|
|
||||||
width: 44px;
|
width: 44px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
color: var(--cs-window-closer-default-fg);
|
color: var(--cs-window-closer-default-fg);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
div.window div.window-header button.window-button-full-screen {
|
||||||
|
right: 90px;
|
||||||
|
}
|
||||||
|
div.window div.window-header button.window-button-close {
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
div.window div.window-header button.window-button-maximize {
|
||||||
|
right: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.window div.window-lock-alert {
|
||||||
|
white-space: normal;
|
||||||
|
text-align: justify;
|
||||||
|
border: var(--border-window-thin);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 5px 5px 5px 5px;
|
||||||
|
background-color: var(--cs-window-default-bg);
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
|
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
|
||||||
/* iPad */
|
/* iPad */
|
||||||
@@ -91,6 +139,7 @@ div.window div.window-header button.window-button-close {
|
|||||||
div.window div.window-header div.window-grab {
|
div.window div.window-header div.window-grab {
|
||||||
height: 35px !important;
|
height: 35px !important;
|
||||||
}
|
}
|
||||||
|
div.window div.window-header button.window-button-full-screen,
|
||||||
div.window div.window-header button.window-button-close {
|
div.window div.window-header button.window-button-close {
|
||||||
height: 40px !important;
|
height: 40px !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ export function Keypad(keys_parent, key_callback) {
|
|||||||
var __isPressed = function(el_key) {
|
var __isPressed = function(el_key) {
|
||||||
let is_pressed = false;
|
let is_pressed = false;
|
||||||
let el_keys = __resolveKeys(el_key);
|
let el_keys = __resolveKeys(el_key);
|
||||||
for (let el_key of el_keys) {
|
for (el_key of el_keys) {
|
||||||
is_pressed = (is_pressed || el_key.classList.contains("pressed"));
|
is_pressed = (is_pressed || el_key.classList.contains("pressed"));
|
||||||
}
|
}
|
||||||
return is_pressed;
|
return is_pressed;
|
||||||
@@ -147,7 +147,7 @@ export function Keypad(keys_parent, key_callback) {
|
|||||||
var __isHolded = function(el_key) {
|
var __isHolded = function(el_key) {
|
||||||
let is_holded = false;
|
let is_holded = false;
|
||||||
let el_keys = __resolveKeys(el_key);
|
let el_keys = __resolveKeys(el_key);
|
||||||
for (let el_key of el_keys) {
|
for (el_key of el_keys) {
|
||||||
is_holded = (is_holded || el_key.classList.contains("holded"));
|
is_holded = (is_holded || el_key.classList.contains("holded"));
|
||||||
}
|
}
|
||||||
return is_holded;
|
return is_holded;
|
||||||
@@ -156,7 +156,7 @@ export function Keypad(keys_parent, key_callback) {
|
|||||||
var __isActive = function(el_key) {
|
var __isActive = function(el_key) {
|
||||||
let is_active = false;
|
let is_active = false;
|
||||||
let el_keys = __resolveKeys(el_key);
|
let el_keys = __resolveKeys(el_key);
|
||||||
for (let el_key of el_keys) {
|
for (el_key of el_keys) {
|
||||||
is_active = (is_active || el_key.classList.contains("pressed") || el_key.classList.contains("holded"));
|
is_active = (is_active || el_key.classList.contains("pressed") || el_key.classList.contains("holded"));
|
||||||
}
|
}
|
||||||
return is_active;
|
return is_active;
|
||||||
@@ -164,14 +164,14 @@ export function Keypad(keys_parent, key_callback) {
|
|||||||
|
|
||||||
var __activate = function(el_key, cls) {
|
var __activate = function(el_key, cls) {
|
||||||
let el_keys = __resolveKeys(el_key);
|
let el_keys = __resolveKeys(el_key);
|
||||||
for (let el_key of el_keys) {
|
for (el_key of el_keys) {
|
||||||
el_key.classList.add(cls);
|
el_key.classList.add(cls);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var __deactivate = function(el_key) {
|
var __deactivate = function(el_key) {
|
||||||
let el_keys = __resolveKeys(el_key);
|
let el_keys = __resolveKeys(el_key);
|
||||||
for (let el_key of el_keys) {
|
for (el_key of el_keys) {
|
||||||
el_key.classList.remove("pressed");
|
el_key.classList.remove("pressed");
|
||||||
el_key.classList.remove("holded");
|
el_key.classList.remove("holded");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ export function Mouse(record_callback) {
|
|||||||
|
|
||||||
var __keypad = null;
|
var __keypad = null;
|
||||||
|
|
||||||
var __current_pos = {x: 0, y:0};
|
var __current_pos = {x: 0, y: 0};
|
||||||
var __sent_pos = {x: 0, y:0};
|
var __sent_pos = {x: 0, y: 0};
|
||||||
var __wheel_delta = {x: 0, y: 0};
|
var __wheel_delta = {x: 0, y: 0};
|
||||||
var __relative_deltas = [];
|
var __relative_deltas = [];
|
||||||
|
|
||||||
@@ -210,15 +210,16 @@ export function Mouse(record_callback) {
|
|||||||
if (__absolute) {
|
if (__absolute) {
|
||||||
let pos = __current_pos;
|
let pos = __current_pos;
|
||||||
if (pos.x !== __sent_pos.x || pos.y !== __sent_pos.y) {
|
if (pos.x !== __sent_pos.x || pos.y !== __sent_pos.y) {
|
||||||
let el_stream_image = $("stream-image");
|
let geometry = $("stream-box").stream_geometry;
|
||||||
let to = {
|
if (geometry) {
|
||||||
x: __translate(pos.x, 0, el_stream_image.clientWidth, -32768, 32767),
|
let to = {
|
||||||
y: __translate(pos.y, 0, el_stream_image.clientHeight, -32768, 32767),
|
x: __translate(pos.x, geometry.x, geometry.width, -32768, 32767),
|
||||||
};
|
y: __translate(pos.y, geometry.y, geometry.height, -32768, 32767),
|
||||||
|
};
|
||||||
tools.debug("Mouse: moved:", to);
|
tools.debug("Mouse: moved:", to);
|
||||||
__sendEvent("mouse_move", {"to": to});
|
__sendEvent("mouse_move", {"to": to});
|
||||||
__sent_pos = pos;
|
__sent_pos = pos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (__relative_deltas.length) {
|
} else if (__relative_deltas.length) {
|
||||||
tools.debug("Mouse: relative:", __relative_deltas);
|
tools.debug("Mouse: relative:", __relative_deltas);
|
||||||
@@ -228,7 +229,13 @@ export function Mouse(record_callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var __translate = function(x, a, b, c, d) {
|
var __translate = function(x, a, b, c, d) {
|
||||||
return Math.round((x - a) / (b - a) * (d - c) + c);
|
let translated = Math.round((x - a) / b * (d - c) + c);
|
||||||
|
if (translated < c) {
|
||||||
|
return c;
|
||||||
|
} else if (translated > d) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
return translated;
|
||||||
};
|
};
|
||||||
|
|
||||||
var __streamWheelHandler = function(event) {
|
var __streamWheelHandler = function(event) {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function Session() {
|
|||||||
var __hid = new Hid();
|
var __hid = new Hid();
|
||||||
var __atx = new Atx();
|
var __atx = new Atx();
|
||||||
var __msd = new Msd();
|
var __msd = new Msd();
|
||||||
var __streamer = new Streamer();
|
var __streamer = new Streamer(__hid);
|
||||||
var __wol = new WakeOnLan();
|
var __wol = new WakeOnLan();
|
||||||
var __gpio = new Gpio();
|
var __gpio = new Gpio();
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ export function Streamer() {
|
|||||||
|
|
||||||
var __resolution = {width: 640, height: 480};
|
var __resolution = {width: 640, height: 480};
|
||||||
|
|
||||||
var __size_factor = 1;
|
|
||||||
|
|
||||||
var __mjpeg_key = tools.makeId();
|
var __mjpeg_key = tools.makeId();
|
||||||
var __mjpeg_id = "";
|
var __mjpeg_id = "";
|
||||||
var __mjpeg_fps = -1;
|
var __mjpeg_fps = -1;
|
||||||
@@ -53,10 +51,6 @@ export function Streamer() {
|
|||||||
|
|
||||||
$("stream-resolution-selector").onchange = (() => __sendParam("resolution", $("stream-resolution-selector").value));
|
$("stream-resolution-selector").onchange = (() => __sendParam("resolution", $("stream-resolution-selector").value));
|
||||||
|
|
||||||
tools.sliderSetParams($("stream-size-slider"), 20, 200, 5, 100);
|
|
||||||
$("stream-size-slider").oninput = () => __resize();
|
|
||||||
$("stream-size-slider").onchange = () => __resize();
|
|
||||||
|
|
||||||
tools.setOnClick($("stream-screenshot-button"), __clickScreenshotButton);
|
tools.setOnClick($("stream-screenshot-button"), __clickScreenshotButton);
|
||||||
tools.setOnClick($("stream-reset-button"), __clickResetButton);
|
tools.setOnClick($("stream-reset-button"), __clickResetButton);
|
||||||
|
|
||||||
@@ -65,6 +59,7 @@ export function Streamer() {
|
|||||||
self.setState(__state_for_invisible);
|
self.setState(__state_for_invisible);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
$("stream-window").resize_hook = __resizeHook;
|
||||||
};
|
};
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
@@ -88,6 +83,11 @@ export function Streamer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state && state.streamer) {
|
if (state && state.streamer) {
|
||||||
|
if (!window.ResizeObserver) {
|
||||||
|
// Browsers that don't support this API(on lower versions of iOS for example)
|
||||||
|
__resizeHook();
|
||||||
|
}
|
||||||
|
|
||||||
if (!$("stream-quality-slider").activated) {
|
if (!$("stream-quality-slider").activated) {
|
||||||
wm.setElementEnabled($("stream-quality-slider"), true);
|
wm.setElementEnabled($("stream-quality-slider"), true);
|
||||||
if ($("stream-quality-slider").value !== state.streamer.encoder.quality) {
|
if ($("stream-quality-slider").value !== state.streamer.encoder.quality) {
|
||||||
@@ -107,13 +107,8 @@ export function Streamer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let resolution_str = __makeStringResolution(state.streamer.source.resolution);
|
let resolution_str = __makeStringResolution(state.streamer.source.resolution);
|
||||||
if (__makeStringResolution(__resolution) != resolution_str) {
|
if (__makeStringResolution(__resolution) !== resolution_str) {
|
||||||
__resolution = state.streamer.source.resolution;
|
__resolution = state.streamer.source.resolution;
|
||||||
if ($("stream-auto-resize-switch").checked) {
|
|
||||||
__adjustSizeFactor();
|
|
||||||
} else {
|
|
||||||
__applySizeFactor();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.features.resolution) {
|
if (state.features.resolution) {
|
||||||
@@ -260,40 +255,17 @@ export function Streamer() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var __resize = function() {
|
var __resizeHook = function() {
|
||||||
let size = $("stream-size-slider").value;
|
let rect = $("stream-image").getBoundingClientRect();
|
||||||
$("stream-size-value").innerHTML = `${size}%`;
|
let width = $("stream-image").naturalWidth;
|
||||||
__size_factor = size / 100;
|
let height = $("stream-image").naturalHeight;
|
||||||
__applySizeFactor();
|
let ratio = Math.min(rect.width / width, rect.height / height);
|
||||||
};
|
$("stream-box").stream_geometry = {
|
||||||
|
"x": Math.round((rect.width - ratio * width) / 2),
|
||||||
var __adjustSizeFactor = function() {
|
"y": Math.round((rect.height - ratio * height) / 2),
|
||||||
let el_window = $("stream-window");
|
"width": Math.round(ratio * width),
|
||||||
let el_slider = $("stream-size-slider");
|
"height": Math.round(ratio * height),
|
||||||
let view = wm.getViewGeometry();
|
};
|
||||||
|
|
||||||
for (let size = 100; size >= el_slider.min; size -= el_slider.step) {
|
|
||||||
tools.info("Stream: adjusting size:", size);
|
|
||||||
$("stream-size-slider").value = size;
|
|
||||||
__resize();
|
|
||||||
|
|
||||||
let 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() {
|
|
||||||
let el = $("stream-image");
|
|
||||||
el.style.width = __resolution.width * __size_factor + "px";
|
|
||||||
el.style.height = __resolution.height * __size_factor + "px";
|
|
||||||
wm.showWindow($("stream-window"), false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var __makeStringResolution = function(resolution) {
|
var __makeStringResolution = function(resolution) {
|
||||||
|
|||||||
@@ -243,5 +243,5 @@ export var tools = new function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export var $ = (id) => document.getElementById(id);
|
export var $ = (id) => document.getElementById(id);
|
||||||
export var $$ = (cls) => document.getElementsByClassName(cls);
|
export var $$ = (cls) => [].slice.call(document.getElementsByClassName(cls));
|
||||||
export var $$$ = (selector) => document.querySelectorAll(selector);
|
export var $$$ = (selector) => document.querySelectorAll(selector);
|
||||||
|
|||||||
@@ -54,14 +54,37 @@ function __WindowManager() {
|
|||||||
__menu_buttons.push(el_button);
|
__menu_buttons.push(el_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!window.ResizeObserver) {
|
||||||
|
tools.error("ResizeObserver not supported");
|
||||||
|
}
|
||||||
|
|
||||||
for (let el_window of $$("window")) {
|
for (let el_window of $$("window")) {
|
||||||
el_window.setAttribute("tabindex", "-1");
|
el_window.setAttribute("tabindex", "-1");
|
||||||
__makeWindowMovable(el_window);
|
__makeWindowMovable(el_window);
|
||||||
__windows.push(el_window);
|
__windows.push(el_window);
|
||||||
|
|
||||||
let el_button = el_window.querySelector(".window-header .window-button-close");
|
if (el_window.classList.contains("window-resizable") && window.ResizeObserver) {
|
||||||
if (el_button) {
|
new ResizeObserver(function() {
|
||||||
tools.setOnClick(el_button, function() {
|
// При переполнении рабочей области сократить размер окна по высоте.
|
||||||
|
// По ширине оно настраивается само в CSS.
|
||||||
|
let view = self.getViewGeometry();
|
||||||
|
let rect = el_window.getBoundingClientRect();
|
||||||
|
if ((rect.bottom - rect.top) > (view.bottom - view.top)) {
|
||||||
|
el_window.style.height = view.bottom - view.top + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el_window.hasAttribute("data-centered")) {
|
||||||
|
__centerWindow(el_window);
|
||||||
|
}
|
||||||
|
if (el_window.resize_hook) {
|
||||||
|
el_window.resize_hook();
|
||||||
|
}
|
||||||
|
}).observe(el_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
let el_close_button = el_window.querySelector(".window-header .window-button-close");
|
||||||
|
if (el_close_button) {
|
||||||
|
tools.setOnClick(el_close_button, function() {
|
||||||
let close_window = function() {
|
let close_window = function() {
|
||||||
__closeWindow(el_window);
|
__closeWindow(el_window);
|
||||||
__activateLastWindow(el_window);
|
__activateLastWindow(el_window);
|
||||||
@@ -78,6 +101,22 @@ function __WindowManager() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let el_maximize_button = el_window.querySelector(".window-header .window-button-maximize");
|
||||||
|
if (el_maximize_button) {
|
||||||
|
tools.setOnClick(el_maximize_button, function() {
|
||||||
|
__maximizeWindow(el_window);
|
||||||
|
__activateLastWindow(el_window);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let el_full_screen_button = el_window.querySelector(".window-header .window-button-full-screen");
|
||||||
|
if (el_full_screen_button) {
|
||||||
|
tools.setOnClick(el_full_screen_button, function() {
|
||||||
|
__fullScreenWindow(el_window);
|
||||||
|
__activateLastWindow(el_window);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onmouseup = __globalMouseButtonHandler;
|
window.onmouseup = __globalMouseButtonHandler;
|
||||||
@@ -86,8 +125,10 @@ function __WindowManager() {
|
|||||||
window.addEventListener("focusin", __focusIn);
|
window.addEventListener("focusin", __focusIn);
|
||||||
window.addEventListener("focusout", __focusOut);
|
window.addEventListener("focusout", __focusOut);
|
||||||
|
|
||||||
window.addEventListener("resize", __organizeWindowsOnResize);
|
window.addEventListener("resize", __organizeWindowsOnBrowserResize);
|
||||||
window.addEventListener("orientationchange", __organizeWindowsOnResize);
|
window.addEventListener("orientationchange", __organizeWindowsOnBrowserResize);
|
||||||
|
|
||||||
|
document.onfullscreenchange = __onFullScreenChange;
|
||||||
};
|
};
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
@@ -205,7 +246,7 @@ function __WindowManager() {
|
|||||||
if (activate) {
|
if (activate) {
|
||||||
__activateWindow(el_window);
|
__activateWindow(el_window);
|
||||||
}
|
}
|
||||||
if (Object.prototype.hasOwnProperty.call(el_window, "show_hook")) {
|
if (el_window.show_hook) {
|
||||||
if (showed) {
|
if (showed) {
|
||||||
el_window.show_hook();
|
el_window.show_hook();
|
||||||
}
|
}
|
||||||
@@ -239,7 +280,7 @@ function __WindowManager() {
|
|||||||
let el_menu = el_button.parentElement.querySelector(".menu");
|
let el_menu = el_button.parentElement.querySelector(".menu");
|
||||||
if (el_button === el_a && window.getComputedStyle(el_menu, null).visibility === "hidden") {
|
if (el_button === el_a && window.getComputedStyle(el_menu, null).visibility === "hidden") {
|
||||||
let rect = el_menu.getBoundingClientRect();
|
let rect = el_menu.getBoundingClientRect();
|
||||||
let offset = self.getViewGeometry().right - (rect.x + el_menu.clientWidth + 2); // + 2 is ugly hack
|
let offset = self.getViewGeometry().right - (rect.left + el_menu.clientWidth + 2); // + 2 is ugly hack
|
||||||
if (offset < 0) {
|
if (offset < 0) {
|
||||||
el_menu.style.right = "0px";
|
el_menu.style.right = "0px";
|
||||||
} else {
|
} else {
|
||||||
@@ -319,7 +360,7 @@ function __WindowManager() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var __organizeWindowsOnResize = function() {
|
var __organizeWindowsOnBrowserResize = function() {
|
||||||
for (let el_window of $$("window")) {
|
for (let el_window of $$("window")) {
|
||||||
if (el_window.style.visibility === "visible") {
|
if (el_window.style.visibility === "visible") {
|
||||||
__organizeWindow(el_window);
|
__organizeWindow(el_window);
|
||||||
@@ -331,10 +372,19 @@ function __WindowManager() {
|
|||||||
let view = self.getViewGeometry();
|
let view = self.getViewGeometry();
|
||||||
let rect = el_window.getBoundingClientRect();
|
let rect = el_window.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (el_window.classList.contains("window-resizable")) {
|
||||||
|
// При переполнении рабочей области сократить размер окна
|
||||||
|
if ((rect.bottom - rect.top) > (view.bottom - view.top)) {
|
||||||
|
el_window.style.height = view.bottom - view.top + "px";
|
||||||
|
}
|
||||||
|
if ((rect.right - rect.left) > (view.right - view.left)) {
|
||||||
|
el_window.style.width = view.right - view.left + "px";
|
||||||
|
}
|
||||||
|
rect = el_window.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
|
||||||
if (el_window.hasAttribute("data-centered") || center) {
|
if (el_window.hasAttribute("data-centered") || center) {
|
||||||
el_window.style.top = Math.max(view.top, Math.round((view.bottom - rect.height) / 2)) + "px";
|
__centerWindow(el_window);
|
||||||
el_window.style.left = Math.round((view.right - rect.width) / 2) + "px";
|
|
||||||
el_window.setAttribute("data-centered", "");
|
|
||||||
} else {
|
} else {
|
||||||
if (rect.top <= view.top) {
|
if (rect.top <= view.top) {
|
||||||
el_window.style.top = view.top + "px";
|
el_window.style.top = view.top + "px";
|
||||||
@@ -350,6 +400,14 @@ function __WindowManager() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var __centerWindow = function(el_window) {
|
||||||
|
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", "");
|
||||||
|
};
|
||||||
|
|
||||||
var __activateLastWindow = function(el_except_window=null) {
|
var __activateLastWindow = function(el_except_window=null) {
|
||||||
let el_last_window = null;
|
let el_last_window = null;
|
||||||
|
|
||||||
@@ -415,6 +473,10 @@ function __WindowManager() {
|
|||||||
let prev_pos = {x: 0, y: 0};
|
let prev_pos = {x: 0, y: 0};
|
||||||
|
|
||||||
function startMoving(event) {
|
function startMoving(event) {
|
||||||
|
// При перетаскивании resizable-окна за правый кран экрана оно ужимается.
|
||||||
|
// Этот костыль фиксит это.
|
||||||
|
el_window.style.width = el_window.offsetWidth + "px";
|
||||||
|
|
||||||
__closeAllMenues();
|
__closeAllMenues();
|
||||||
__activateWindow(el_window);
|
__activateWindow(el_window);
|
||||||
event = (event || window.event);
|
event = (event || window.event);
|
||||||
@@ -474,5 +536,45 @@ function __WindowManager() {
|
|||||||
el_grab.ontouchstart = startMoving;
|
el_grab.ontouchstart = startMoving;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var __onFullScreenChange = function(event) {
|
||||||
|
let el_window = event.target;
|
||||||
|
if (!document.fullscreenElement) {
|
||||||
|
el_window.style.padding = "";
|
||||||
|
let rect = el_window.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";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
el_window.style.padding = "0px 0px 0px 0px";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var __fullScreenWindow = function(el_window) {
|
||||||
|
el_window.before_full_screen = el_window.getBoundingClientRect();
|
||||||
|
el_window.requestFullscreen();
|
||||||
|
if ("keyboard" in navigator && "lock" in navigator.keyboard) {
|
||||||
|
navigator.keyboard.lock();
|
||||||
|
} else {
|
||||||
|
let el_lock_alert = el_window.querySelector(".window-lock-alert");
|
||||||
|
if (el_lock_alert) {
|
||||||
|
tools.hiddenSetVisible(el_lock_alert, true);
|
||||||
|
setTimeout(function() {
|
||||||
|
tools.hiddenSetVisible(el_lock_alert, false);
|
||||||
|
}, 7000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var __maximizeWindow = function(el_window) {
|
||||||
|
let vertical_offset = $("navbar").offsetHeight;
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
|
||||||
__init__();
|
__init__();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user