diff --git a/app/images/alt.png b/app/images/alt.png deleted file mode 100644 index d42af7b4..00000000 Binary files a/app/images/alt.png and /dev/null differ diff --git a/app/images/alt.svg b/app/images/alt.svg new file mode 100644 index 00000000..e5bb4612 --- /dev/null +++ b/app/images/alt.svg @@ -0,0 +1,92 @@ + + + + diff --git a/app/images/clipboard.png b/app/images/clipboard.png deleted file mode 100644 index 24df33c1..00000000 Binary files a/app/images/clipboard.png and /dev/null differ diff --git a/app/images/clipboard.svg b/app/images/clipboard.svg new file mode 100644 index 00000000..79af2752 --- /dev/null +++ b/app/images/clipboard.svg @@ -0,0 +1,106 @@ + + + + diff --git a/app/images/connect.png b/app/images/connect.png deleted file mode 100644 index 79e71adb..00000000 Binary files a/app/images/connect.png and /dev/null differ diff --git a/app/images/connect.svg b/app/images/connect.svg new file mode 100644 index 00000000..56cde414 --- /dev/null +++ b/app/images/connect.svg @@ -0,0 +1,96 @@ + + + + diff --git a/app/images/ctrl.png b/app/images/ctrl.png deleted file mode 100644 index a63b601f..00000000 Binary files a/app/images/ctrl.png and /dev/null differ diff --git a/app/images/ctrl.svg b/app/images/ctrl.svg new file mode 100644 index 00000000..856e9395 --- /dev/null +++ b/app/images/ctrl.svg @@ -0,0 +1,96 @@ + + + + diff --git a/app/images/ctrlaltdel.png b/app/images/ctrlaltdel.png deleted file mode 100644 index 31922e53..00000000 Binary files a/app/images/ctrlaltdel.png and /dev/null differ diff --git a/app/images/ctrlaltdel.svg b/app/images/ctrlaltdel.svg new file mode 100644 index 00000000..d7744ea3 --- /dev/null +++ b/app/images/ctrlaltdel.svg @@ -0,0 +1,100 @@ + + + + diff --git a/app/images/disconnect.png b/app/images/disconnect.png deleted file mode 100644 index 8832f5ea..00000000 Binary files a/app/images/disconnect.png and /dev/null differ diff --git a/app/images/disconnect.svg b/app/images/disconnect.svg new file mode 100644 index 00000000..6be7d187 --- /dev/null +++ b/app/images/disconnect.svg @@ -0,0 +1,94 @@ + + + + diff --git a/app/images/drag.png b/app/images/drag.png deleted file mode 100644 index 433f896d..00000000 Binary files a/app/images/drag.png and /dev/null differ diff --git a/app/images/drag.svg b/app/images/drag.svg new file mode 100644 index 00000000..139caf94 --- /dev/null +++ b/app/images/drag.svg @@ -0,0 +1,76 @@ + + + + diff --git a/app/images/error.svg b/app/images/error.svg new file mode 100644 index 00000000..6c47e8f2 --- /dev/null +++ b/app/images/error.svg @@ -0,0 +1,81 @@ + + + + diff --git a/app/images/esc.png b/app/images/esc.png deleted file mode 100644 index ece5f7cb..00000000 Binary files a/app/images/esc.png and /dev/null differ diff --git a/app/images/esc.svg b/app/images/esc.svg new file mode 100644 index 00000000..830152b5 --- /dev/null +++ b/app/images/esc.svg @@ -0,0 +1,92 @@ + + + + diff --git a/app/images/fullscreen.png b/app/images/fullscreen.png deleted file mode 100644 index f4fa0ce8..00000000 Binary files a/app/images/fullscreen.png and /dev/null differ diff --git a/app/images/fullscreen.svg b/app/images/fullscreen.svg new file mode 100644 index 00000000..29bd05da --- /dev/null +++ b/app/images/fullscreen.svg @@ -0,0 +1,93 @@ + + + + diff --git a/app/images/handle.svg b/app/images/handle.svg new file mode 100644 index 00000000..4a7a126f --- /dev/null +++ b/app/images/handle.svg @@ -0,0 +1,82 @@ + + + + diff --git a/app/images/handle_bg.svg b/app/images/handle_bg.svg new file mode 100644 index 00000000..b4c4d131 --- /dev/null +++ b/app/images/handle_bg.svg @@ -0,0 +1,172 @@ + + + + diff --git a/app/images/info.svg b/app/images/info.svg new file mode 100644 index 00000000..21d268c8 --- /dev/null +++ b/app/images/info.svg @@ -0,0 +1,87 @@ + + + + diff --git a/app/images/keyboard.png b/app/images/keyboard.png deleted file mode 100644 index f7979525..00000000 Binary files a/app/images/keyboard.png and /dev/null differ diff --git a/app/images/keyboard.svg b/app/images/keyboard.svg new file mode 100644 index 00000000..137b350a --- /dev/null +++ b/app/images/keyboard.svg @@ -0,0 +1,88 @@ + + + + diff --git a/app/images/mouse_left.png b/app/images/mouse_left.png deleted file mode 100644 index 1de7a486..00000000 Binary files a/app/images/mouse_left.png and /dev/null differ diff --git a/app/images/mouse_left.svg b/app/images/mouse_left.svg new file mode 100644 index 00000000..ce4cca41 --- /dev/null +++ b/app/images/mouse_left.svg @@ -0,0 +1,92 @@ + + + + diff --git a/app/images/mouse_middle.png b/app/images/mouse_middle.png deleted file mode 100644 index 81fbd9bd..00000000 Binary files a/app/images/mouse_middle.png and /dev/null differ diff --git a/app/images/mouse_middle.svg b/app/images/mouse_middle.svg new file mode 100644 index 00000000..6603425c --- /dev/null +++ b/app/images/mouse_middle.svg @@ -0,0 +1,92 @@ + + + + diff --git a/app/images/mouse_none.png b/app/images/mouse_none.png deleted file mode 100644 index 93dbf578..00000000 Binary files a/app/images/mouse_none.png and /dev/null differ diff --git a/app/images/mouse_none.svg b/app/images/mouse_none.svg new file mode 100644 index 00000000..3e0f838a --- /dev/null +++ b/app/images/mouse_none.svg @@ -0,0 +1,92 @@ + + + + diff --git a/app/images/mouse_right.png b/app/images/mouse_right.png deleted file mode 100644 index 355b25dc..00000000 Binary files a/app/images/mouse_right.png and /dev/null differ diff --git a/app/images/mouse_right.svg b/app/images/mouse_right.svg new file mode 100644 index 00000000..f4bad767 --- /dev/null +++ b/app/images/mouse_right.svg @@ -0,0 +1,92 @@ + + + + diff --git a/app/images/power.png b/app/images/power.png deleted file mode 100644 index f68fd081..00000000 Binary files a/app/images/power.png and /dev/null differ diff --git a/app/images/power.svg b/app/images/power.svg new file mode 100644 index 00000000..4925d3e8 --- /dev/null +++ b/app/images/power.svg @@ -0,0 +1,87 @@ + + + + diff --git a/app/images/settings.png b/app/images/settings.png deleted file mode 100644 index a43f5e10..00000000 Binary files a/app/images/settings.png and /dev/null differ diff --git a/app/images/settings.svg b/app/images/settings.svg new file mode 100644 index 00000000..dbb2e80a --- /dev/null +++ b/app/images/settings.svg @@ -0,0 +1,76 @@ + + + + diff --git a/app/images/tab.png b/app/images/tab.png deleted file mode 100644 index 84134872..00000000 Binary files a/app/images/tab.png and /dev/null differ diff --git a/app/images/tab.svg b/app/images/tab.svg new file mode 100644 index 00000000..1ccb3229 --- /dev/null +++ b/app/images/tab.svg @@ -0,0 +1,86 @@ + + + + diff --git a/app/images/toggleextrakeys.png b/app/images/toggleextrakeys.png deleted file mode 100644 index ad8e0a70..00000000 Binary files a/app/images/toggleextrakeys.png and /dev/null differ diff --git a/app/images/toggleextrakeys.svg b/app/images/toggleextrakeys.svg new file mode 100644 index 00000000..b578c0d4 --- /dev/null +++ b/app/images/toggleextrakeys.svg @@ -0,0 +1,90 @@ + + + + diff --git a/app/images/warning.svg b/app/images/warning.svg new file mode 100644 index 00000000..7114f9b1 --- /dev/null +++ b/app/images/warning.svg @@ -0,0 +1,81 @@ + + + + diff --git a/app/styles/auto.css b/app/styles/auto.css new file mode 100644 index 00000000..50f9a822 --- /dev/null +++ b/app/styles/auto.css @@ -0,0 +1,89 @@ +/* + * noVNC auto CSS + * Copyright (C) 2012 Joel Martin + * Copyright (C) 2016 Samuel Mannehed for Cendio AB + * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) + * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). + */ + +body { + margin:0; + padding:0; + font-family: Helvetica; + /*Background image with light grey curve.*/ + background-color:#494949; + background-repeat:no-repeat; + background-position:right bottom; + height:100%; +} + +html { + height:100%; +} + +#noVNC_container { + display: table; + width:100%; + height:100%; + background-color:#313131; + border-bottom-right-radius: 800px 600px; + /*border-top-left-radius: 800px 600px;*/ +} + +#noVNC_status { + font-size: 12px; + padding-top: 4px; + height:32px; + text-align: center; + font-weight: bold; + color: #fff; + z-index: 0; + position: absolute; + width: 100%; + margin-left: 0px; +} + +.noVNC_status_normal { + background: #b2bdcd; /* Old browsers */ + background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ +} + +.noVNC_status_error { + background: #f04040; /* Old browsers */ + background: -moz-linear-gradient(top, #f04040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + background: linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ +} + +.noVNC_status_warn { + background: #f0f040; /* Old browsers */ + background: -moz-linear-gradient(top, #f0f040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + background: linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ +} + +#noVNC_buttons { + white-space: nowrap; +} + +/* Do not set width/height for VNC_canvas or incorrect + * scaling will occur. Canvas size depends on remote VNC + * settings and noVNC settings. */ +#noVNC_canvas { + position: absolute; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; +} diff --git a/app/styles/base.css b/app/styles/base.css index 382234e3..4431908d 100644 --- a/app/styles/base.css +++ b/app/styles/base.css @@ -2,6 +2,7 @@ * noVNC base CSS * Copyright (C) 2012 Joel Martin * Copyright (C) 2016 Samuel Mannehed for Cendio AB + * Copyright (C) 2016 Pierre Ossman for Cendio AB * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). */ @@ -21,119 +22,506 @@ html { height:100%; } -#noVNC_controls ul { - list-style: none; - margin: 0px; - padding: 0px; -} -#noVNC_controls li { - padding-bottom:8px; +.noVNC_only_touch.noVNC_hidden { + display: none; } -#noVNC_setting_host { - width:150px; -} -#noVNC_setting_port { - width: 80px; -} -#noVNC_setting_password { - width: 150px; -} -#noVNC_setting_encrypt { -} -#noVNC_setting_path { - width: 100px; -} -#noVNC_connect_button { - width: 110px; - float:right; +/* ---------------------------------------- + * Input Elements + * ---------------------------------------- + */ + +input[type=input], input[type=password], input:not([type]), textarea { + /* Disable default rendering */ + -webkit-appearance: none; + -moz-appearance: none; + background: none; + + margin: 2px; + padding: 2px; + border: 1px solid rgb(192, 192, 192); + border-radius: 5px; + color: black; + background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240)); } -#noVNC_buttons { +input[type=button], select { + /* Disable default rendering */ + -webkit-appearance: none; + -moz-appearance: none; + background: none; + + margin: 2px; + padding: 2px; + border: 1px solid rgb(192, 192, 192); + border-bottom-width: 2px; + border-radius: 5px; + color: black; + background: linear-gradient(to top, rgb(255, 255, 255), rgb(240, 240, 240)); + + /* This avoids it jumping around when :active */ + vertical-align: middle; +} + +input[type=button] { + padding-left: 20px; + padding-right: 20px; +} + +option { + color: black; + background: white; +} + +input[type=input]:focus, input[type=password]:focus, +input:not([type]):focus, input[type=button]:focus, +textarea:focus, select:focus { + box-shadow: 0px 0px 3px rgba(74, 144, 217, 0.5); + border-color: rgb(74, 144, 217); + outline: none; +} + +input[type=button]::-moz-focus-inner { + border: none; +} + +input[type=input]:disabled, input[type=password]:disabled, +input:not([type]):disabled, input[type=button]:disabled, +textarea:disabled, select:disabled { + color: rgb(128, 128, 128); + background: rgb(240, 240, 240); +} + +input[type=button]:active, select:active { + border-bottom-width: 1px; + margin-top: 3px; +} + +:root:not(.noVNC_touch) input[type=button]:hover:not(:disabled), :root:not(.noVNC_touch) select:hover:not(:disabled) { + background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250)); +} + +/* ---------------------------------------- + * WebKit centering hacks + * ---------------------------------------- + */ + +.noVNC_center { + /* + * This is a workaround because webkit misrenders transforms and + * uses non-integer coordinates, resulting in blurry content. + * Ideally we'd use "top: 50%; transform: translateY(-50%);" on + * the objects instead. + */ + display: flex; + align-items: center; + justify-content: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; +} +.noVNC_center > * { + pointer-events: auto; +} +.noVNC_vcenter { + display: flex; + flex-direction: column; + justify-content: center; + position: fixed; + top: 0; + left: 0; + height: 100%; + pointer-events: none; +} +.noVNC_vcenter > * { + pointer-events: auto; +} + +/* ---------------------------------------- + * Control Bar + * ---------------------------------------- + */ + +#noVNC_control_bar_anchor { + /* The anchor is needed to get z-stacking to work */ + position: fixed; + z-index: 2; + + transition: 0.5s ease-in-out; + + /* Edge misrenders animations wihthout this */ + transform: translateX(0); +} +:root.noVNC_connected #noVNC_control_bar_anchor.noVNC_idle { + opacity: 0.8; +} + +#noVNC_control_bar { + position: relative; + /* left: calc(-35px - 10px - 5px - 30px), but IE doesn't animate calc */ + left: -80px; + + transition: 0.5s ease-in-out; + + background-color: rgb(110, 132, 163); + border-radius: 0 10px 10px 0; + + /* The extra border is to get a proper shadow */ + border-color: rgb(110, 132, 163); + border-style: solid; + border-width: 0 0 0 30px; +} +#noVNC_control_bar.noVNC_open { + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); + left: -30px; +} + +#noVNC_control_bar_handle { + position: absolute; + right: -15px; + top: 0; + transform: translateY(35px); + width: 50px; + height: 50px; + z-index: -2; + cursor: pointer; + border-radius: 0 5px 5px 0; + background-color: rgb(83, 99, 122); + background-image: url("../images/handle_bg.svg"); + background-repeat: no-repeat; + background-position: right; + box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.5); +} +#noVNC_control_bar_handle:after { + content: ""; + transition: 0.5s ease-in-out; + background: url("../images/handle.svg"); + position: absolute; + top: 22px; /* (50px-6px)/2 */ + right: 5px; + width: 5px; + height: 6px; +} +#noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after { + transform: translateX(1px) rotate(180deg); +} +:root:not(.noVNC_connected) #noVNC_control_bar_handle { + display: none; +} +#noVNC_control_bar_handle div { + position: absolute; + right: -35px; + top: 0; + width: 50px; + height: 50px; +} +:root:not(.noVNC_touch) #noVNC_control_bar_handle div { + display: none; +} + +#noVNC_control_bar .noVNC_scroll { + max-height: 100vh; /* Chrome is buggy with 100% */ + overflow-x: hidden; + overflow-y: auto; + padding: 0 10px 0 5px; +} + +/* General button style */ +.noVNC_button { + display: block; + padding: 4px 4px; + margin: 10px 0; + vertical-align: middle; + border:1px solid rgba(255, 255, 255, 0.2); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.noVNC_button.noVNC_selected { + border-color: rgba(0, 0, 0, 0.8); + background: rgba(0, 0, 0, 0.5); +} +.noVNC_button:disabled { + opacity: 0.4; +} +.noVNC_button:focus { + outline: none; +} +.noVNC_button:active { + padding-top: 5px; + padding-bottom: 3px; +} +:root:not(.noVNC_touch) .noVNC_button.noVNC_selected:hover { + border-color: rgba(0, 0, 0, 0.4); + background: rgba(0, 0, 0, 0.2); +} +:root:not(.noVNC_touch) .noVNC_button:hover { + background: rgba(255, 255, 255, 0.2); +} +.noVNC_button.noVNC_hidden { + display: none; +} + +/* Panels */ +.noVNC_panel { + transform: translateX(25px); + + transition: 0.5s ease-in-out; + + max-height: 100vh; /* Chrome is buggy with 100% */ + overflow-x: hidden; + overflow-y: auto; + + visibility: hidden; + opacity: 0; + + padding: 15px; + + background: #fff; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + color: #000; + border: 2px solid #E0E0E0; + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); +} +.noVNC_panel.noVNC_open { + visibility: visible; + opacity: 1; + transform: translateX(75px); +} + +.noVNC_panel hr { + border: none; + border-top: 1px solid rgb(192, 192, 192); +} + +.noVNC_panel label { + display: block; +} + +.noVNC_panel .noVNC_heading { + background-color: rgb(110, 132, 163); + border-radius: 5px; + padding: 5px; + /* Compensate for padding in image */ + padding-right: 8px; + color: white; + font-size: 20px; + margin-bottom: 10px; white-space: nowrap; } +.noVNC_panel .noVNC_heading img { + vertical-align: bottom; +} -#noVNC_view_drag_button { +.noVNC_submit { + float: right; +} + +:root:not(.noVNC_connected) #noVNC_view_drag_button { display: none; } -#noVNC_sendCtrlAltDel_button { + +/* noVNC Touch Device only buttons */ +:root:not(.noVNC_connected) #noVNC_mobile_buttons { display: none; } -#noVNC_fullscreen_button { +:root:not(.noVNC_touch) #noVNC_mobile_buttons { display: none; } + +#noVNC_keyboardinput { + width: 1px; + height: 1px; + background-color: #fff; + color: #fff; + border: 0; + position: relative; + left: -40px; + z-index: -1; + ime-mode: disabled; +} + +/* Extra manual keys */ +:root:not(.noVNC_connected) #noVNC_extra_keys { + display: none; +} + +#noVNC_modifiers { + background-color: rgb(92, 92, 92); + border: none; + padding: 0 10px; +} + +/* XVP Shutdown/Reboot */ +:root:not(.noVNC_connected) #noVNC_xvp_button { + display: none; +} +#noVNC_xvp { +} #noVNC_xvp_buttons { display: none; } -#noVNC_mobile_buttons { + +#noVNC_xvp input[type=button] { + width: 100%; +} + +/* Clipboard */ +:root:not(.noVNC_connected) #noVNC_clipboard_button { display: none; } +#noVNC_clipboard { + /* Full screen, minus padding and left and right margins */ + max-width: calc(100vw - 2*15px - 75px - 25px); +} +#noVNC_clipboard_text { + width: 500px; + max-width: 100%; +} -#noVNC_extra_keys { - display: inline; - list-style-type: none; - padding: 0px; +/* Settings */ +#noVNC_settings { +} +#noVNC_settings ul { + list-style: none; margin: 0px; - position: relative; + padding: 0px; +} +#noVNC_setting_path { + width: 100px; } -.noVNC_buttons_left { - float: left; - z-index: 1; - position: relative; +/* Connection Controls */ +:root.noVNC_connected #noVNC_connect_controls_button { + display: none; +} +:root:not(.noVNC_connected) #noVNC_disconnect_button { + display: none; +} +#noVNC_connect_controls ul { + list-style: none; + margin: 0px; + padding: 0px; +} +#noVNC_setting_port { + width: 80px; } -.noVNC_buttons_right { - float:right; - right: 0px; - z-index: 2; - position: absolute; -} +/* ---------------------------------------- + * Status Dialog + * ---------------------------------------- + */ #noVNC_status { - font-size: 12px; - padding-top: 4px; - height:32px; - text-align: center; - font-weight: bold; + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 3; + transform: translateY(-100%); + + transition: 0.5s ease-in-out; + + visibility: hidden; + opacity: 0; + + padding: 5px; + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + + line-height: 25px; + word-wrap: break-word; color: #fff; + + border-bottom: 1px solid rgba(0, 0, 0, 0.9); +} +#noVNC_status.noVNC_open { + transform: translateY(0); + visibility: visible; + opacity: 1; } -#noVNC_settings_menu { - margin: 3px; - text-align: left; +#noVNC_status::before { + content: ""; + display: inline-block; + width: 25px; + height: 25px; + margin-right: 5px; } -#noVNC_settings_menu ul { + +#noVNC_status.noVNC_status_normal { + background: rgba(128,128,128,0.9); +} +#noVNC_status.noVNC_status_normal::before { + content: url("../images/info.svg") " "; +} +#noVNC_status.noVNC_status_error { + background: rgba(200,55,55,0.9); +} +#noVNC_status.noVNC_status_error::before { + content: url("../images/error.svg") " "; +} +#noVNC_status.noVNC_status_warn { + background: rgba(180,180,30,0.9); +} +#noVNC_status.noVNC_status_warn::before { + content: url("../images/warning.svg") " "; +} + +/* ---------------------------------------- + * Password Dialog + * ---------------------------------------- + */ + +#noVNC_password_dlg { + transform: translateY(-50px); +} +#noVNC_password_dlg.noVNC_open { + transform: translateY(0); +} +#noVNC_password_dlg ul { list-style: none; margin: 0px; padding: 0px; } -#noVNC_settings_apply { - float:right; -} +/* ---------------------------------------- + * Main Area + * ---------------------------------------- + */ #noVNC_container { display: table; - width:100%; - height:100%; - background-color:#313131; + width: 100%; + height: 100%; + background-color: #313131; border-bottom-right-radius: 800px 600px; /*border-top-left-radius: 800px 600px;*/ } +/* HTML5 Canvas */ #noVNC_screen { - display: none; position: absolute; margin: 0px; padding: 0px; bottom: 0px; - top: 36px; /* the height of the control bar */ + top: 0px; left: 0px; right: 0px; width: auto; height: auto; } +:root:not(.noVNC_connected) #noVNC_screen { + display: none; +} /* Do not set width/height for VNC_canvas or incorrect * scaling will occur. Canvas size depends on remote VNC @@ -146,239 +534,6 @@ html { margin-right: auto; } -#VNC_clipboard_clear_button { - float:right; -} -#VNC_clipboard_text { - font-size: 11px; -} - -#noVNC_clipboard_clear_button { - float:right; -} - -/*Bubble contents divs*/ -#noVNC_settings { - display:none; - margin-top:73px; - right:20px; - position:fixed; -} - -#noVNC_controls { - display:none; - margin-top:73px; - right:12px; - position:fixed; -} -#noVNC_controls.top:after { - right:15px; -} - -#noVNC_description { - display:none; - position:fixed; - - margin-top:73px; - right:20px; - left:20px; - padding:15px; - color:#000; - background:#eee; /* default background for browsers without gradient support */ - - border:2px solid #E0E0E0; - -webkit-border-radius:10px; - -moz-border-radius:10px; - border-radius:10px; -} - -#noVNC_popup_status { - display:none; - position: fixed; - z-index: 1; - - margin:15px; - margin-top:60px; - padding:15px; - width:auto; - - text-align:center; - font-weight:bold; - word-wrap:break-word; - color:#fff; - background:rgba(0,0,0,0.65); - - -webkit-border-radius:10px; - -moz-border-radius:10px; - border-radius:10px; -} - -#noVNC_xvp { - display:none; - margin-top:73px; - right:30px; - position:fixed; -} -#noVNC_xvp.top:after { - right:125px; -} - -#noVNC_clipboard { - display:none; - margin-top:73px; - right:30px; - position:fixed; -} -#noVNC_clipboard.top:after { - right:85px; -} - -#noVNC_keyboardinput { - width:1px; - height:1px; - background-color:#fff; - color:#fff; - border:0; - position: relative; - left: -40px; - z-index: -1; - ime-mode: disabled; -} - -/* - * Advanced Styling - */ - -.noVNC_status_normal { - background: #b2bdcd; /* Old browsers */ - background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ - background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ -} -.noVNC_status_error { - background: #f04040; /* Old browsers */ - background: -moz-linear-gradient(top, #f04040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ - background: linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ -} -.noVNC_status_warn { - background: #f0f040; /* Old browsers */ - background: -moz-linear-gradient(top, #f0f040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ - background: linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ -} - -/* Control bar */ -#noVNC_control_bar { - position:fixed; - - display:block; - height:36px; - left:0; - top:0; - width:100%; - z-index:200; -} - -.noVNC_status_button { - padding: 4px 4px; - vertical-align: middle; - border:1px solid #869dbc; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - background: #b2bdcd; /* Old browsers */ - background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */ - background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ - /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ -} - -.noVNC_status_button_selected { - padding: 4px 4px; - vertical-align: middle; - border:1px solid #4366a9; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - background: #779ced; /* Old browsers */ - background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */ - background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */ - /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ -} - -.noVNC_status_button:disabled { - opacity: 0.4; -} - - -/*Settings Bubble*/ -.triangle-right { - position:relative; - padding:15px; - margin:1em 0 3em; - color:#fff; - background:#fff; /* default background for browsers without gradient support */ - /* css3 */ - /*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698)); - background:-moz-linear-gradient(#2e88c4, #075698); - background:-o-linear-gradient(#2e88c4, #075698); - background:linear-gradient(#2e88c4, #075698);*/ - -webkit-border-radius:10px; - -moz-border-radius:10px; - border-radius:10px; - color:#000; - border:2px solid #E0E0E0; -} - -.triangle-right.top:after { - border-color: transparent #E0E0E0; - border-width: 20px 20px 0 0; - bottom: auto; - left: auto; - right: 50px; - top: -20px; -} - -.triangle-right:after { - content:""; - position:absolute; - bottom:-20px; /* value = - border-top-width - border-bottom-width */ - left:50px; /* controls horizontal position */ - border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */ - border-style:solid; - border-color:#E0E0E0 transparent; - /* reduce the damage in FF3.0 */ - display:block; - width:0; -} - -.triangle-right.top:after { - top:-40px; /* value = - border-top-width - border-bottom-width */ - right:50px; /* controls horizontal position */ - bottom:auto; - left:auto; - border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */ - border-color:transparent #E0E0E0; -} - /*Default noVNC logo.*/ /* From: http://fonts.googleapis.com/css?family=Orbitron:700 */ @font-face { @@ -389,13 +544,22 @@ html { url('Orbitron700.ttf') format('truetype'); } -#noVNC_logo { - margin-top: 170px; - margin-left: 10px; +.noVNC_logo { color:yellow; text-align:left; font-family: 'Orbitron', 'OrbitronTTF', sans-serif; + font-size: 13px; line-height:90%; + text-shadow: 1px 1px 0 #000; +} +.noVNC_logo span{ + color:green; +} + +#noVNC_logo { + margin-top: 60px; + margin-left: 60px; + font-size: 180px; text-shadow: 5px 5px 0 #000, -1px -1px 0 #000, @@ -403,10 +567,8 @@ html { -1px 1px 0 #000, 1px 1px 0 #000; } - - -#noVNC_logo span{ - color:green; +:root.noVNC_connected #noVNC_logo { + display: none; } /* ---------------------------------------- @@ -414,113 +576,19 @@ html { * ---------------------------------------- */ - -.noVNC_status_button { - font-size: 12px; -} - -#noVNC_clipboard_text { - width: 500px; -} - -#noVNC_logo { - font-size: 180px; -} - -.noVNC_buttons_left { - padding-left: 10px; -} - -.noVNC_buttons_right { - padding-right: 10px; -} - -#noVNC_status { - z-index: 0; - position: absolute; - width: 100%; - margin-left: 0px; -} - -#noVNC_toggleExtraKeys_button { display: none; } -#noVNC_toggleCtrl_button { display: inline; } -#noVNC_toggleAlt_button { display: inline; } -#noVNC_sendTab_button { display: inline; } -#noVNC_sendEsc_button { display: inline; } - -/* left-align the status text on lower resolutions */ -@media screen and (max-width: 800px){ - #noVNC_status { - z-index: 1; - position: relative; - width: auto; - float: left; - margin-left: 4px; - } -} - @media screen and (max-width: 640px){ - #noVNC_clipboard_text { - width: 410px; - } #noVNC_logo { font-size: 150px; } - .noVNC_status_button { - font-size: 10px; - } - .noVNC_buttons_left { - padding-left: 0px; - } - .noVNC_buttons_right { - padding-right: 0px; - } - /* collapse the extra keys on lower resolutions */ - #noVNC_toggleExtraKeys_button { - display: inline; - } - #noVNC_toggleCtrl_button { - display: none; - position: absolute; - top: 30px; - left: 0px; - } - #noVNC_toggleAlt_button { - display: none; - position: absolute; - top: 65px; - left: 0px; - } - #noVNC_sendTab_button { - display: none; - position: absolute; - top: 100px; - left: 0px; - } - #noVNC_sendEsc_button { - display: none; - position: absolute; - top: 135px; - left: 0px; - } } @media screen and (min-width: 321px) and (max-width: 480px) { - #noVNC_clipboard_text { - width: 250px; - } #noVNC_logo { font-size: 110px; } } @media screen and (max-width: 320px) { - .noVNC_status_button { - font-size: 9px; - } - #noVNC_clipboard_text { - width: 220px; - } #noVNC_logo { font-size: 90px; } diff --git a/app/styles/black.css b/app/styles/black.css index 5c4558dc..178fa100 100644 --- a/app/styles/black.css +++ b/app/styles/black.css @@ -6,66 +6,16 @@ * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). */ -#noVNC_keyboardinput { - background-color:#000; -} - -.noVNC_status_normal { - background: #4c4c4c; /* Old browsers */ - background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ - background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ -} -.noVNC_status_error { - background: #f04040; /* Old browsers */ - background: -moz-linear-gradient(top, #f04040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ - background: linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ -} -.noVNC_status_warn { - background: #f0f040; /* Old browsers */ - background: -moz-linear-gradient(top, #f0f040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ - background: linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ -} - -.triangle-right { +.noVNC_panel { border:2px solid #fff; background:#000; color:#fff; } -.noVNC_status_button { - font-size: 12px; - vertical-align: middle; - border:1px solid #4c4c4c; - - background: #4c4c4c; /* Old browsers */ - background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */ - background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +#noVNC_control_bar, #noVNC_control_bar_handle, .noVNC_panel .noVNC_heading { + background: #4c4c4c; } -.noVNC_status_button_selected { - background: #9dd53a; /* Old browsers */ - background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */ - background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */ +.noVNC_button.noVNC_selected { + background: #9dd53a; } diff --git a/app/styles/blue.css b/app/styles/blue.css index 4ab53bde..a38ede15 100644 --- a/app/styles/blue.css +++ b/app/styles/blue.css @@ -6,59 +6,12 @@ * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). */ -.noVNC_status_normal { - background-color:#04073d; - background-image: -webkit-gradient( - linear, - left bottom, - left top, - color-stop(0.54, rgb(10,15,79)), - color-stop(0.5, rgb(4,7,61)) - ); - background-image: -moz-linear-gradient( - center bottom, - rgb(10,15,79) 54%, - rgb(4,7,61) 50% - ); -} -.noVNC_status_error { - background-color:#f04040; - background-image: -webkit-gradient( - linear, - left bottom, - left top, - color-stop(0.54, rgb(240,64,64)), - color-stop(0.5, rgb(4,7,61)) - ); - background-image: -moz-linear-gradient( - center bottom, - rgb(4,7,61) 54%, - rgb(249,64,64) 50% - ); -} -.noVNC_status_warn { - background-color:#f0f040; - background-image: -webkit-gradient( - linear, - left bottom, - left top, - color-stop(0.54, rgb(240,240,64)), - color-stop(0.5, rgb(4,7,61)) - ); - background-image: -moz-linear-gradient( - center bottom, - rgb(4,7,61) 54%, - rgb(240,240,64) 50% - ); -} - -.triangle-right { +.noVNC_panel { border:2px solid #fff; background:#04073d; color:#fff; } -#noVNC_keyboardinput { - background-color:#04073d; +#noVNC_control_bar, #noVNC_control_bar_handle, .noVNC_panel .noVNC_heading { + background: #080F80; } - diff --git a/app/ui.js b/app/ui.js index 78d397d7..cfcfe1c5 100644 --- a/app/ui.js +++ b/app/ui.js @@ -2,6 +2,7 @@ * noVNC: HTML5 VNC client * Copyright (C) 2012 Joel Martin * Copyright (C) 2016 Samuel Mannehed for Cendio AB + * Copyright (C) 2016 Pierre Ossman for Cendio AB * Licensed under MPL 2.0 (see LICENSE.txt) * * See README.md for usage and integration instructions. @@ -38,14 +39,16 @@ var UI; rfb_state: 'loaded', resizeTimeout: null, - popupStatusTimeout: null, + statusTimeout: null, hideKeyboardTimeout: null, + idleControlbarTimeout: null, + closeControlbarTimeout: null, - settingsOpen: false, - connSettingsOpen: false, - clipboardOpen: false, + controlbarGrabbed: false, + controlbarDrag: false, + controlbarMouseDownClientY: 0, + controlbarMouseDownOffsetY: 0, keyboardVisible: false, - extraKeysVisible: false, isTouchDevice: false, isSafari: false, @@ -53,9 +56,6 @@ var UI; lastKeyboardinput: null, defaultKeyboardinputLen: 100, - ctrlOn: false, - altOn: false, - // Setup rfb object, load settings from browser storage, then call // UI.init to setup the UI/menus load: function(callback) { @@ -64,8 +64,60 @@ var UI; // Render default UI and initialize settings menu start: function(callback) { - UI.isTouchDevice = 'ontouchstart' in document.documentElement; + // Setup global variables first + UI.isTouchDevice = 'ontouchstart' in document.documentElement; + UI.isSafari = (navigator.userAgent.indexOf('Safari') !== -1 && + navigator.userAgent.indexOf('Chrome') === -1); + + UI.initSettings(); + + // Adapt the interface for touch screen devices + if (UI.isTouchDevice) { + document.documentElement.classList.add("noVNC_touch"); + // Remove the address bar + setTimeout(function() { window.scrollTo(0, 1); }, 100); + UI.forceSetting('clip', true); + } else { + UI.initSetting('clip', false); + } + + // Setup and initialize event handlers + UI.setupWindowEvents(); + UI.setupFullscreen(); + UI.addControlbarHandlers(); + UI.addTouchSpecificHandlers(); + UI.addExtraKeysHandlers(); + UI.addXvpHandlers(); + UI.addConnectionControlHandlers(); + UI.addClipboardHandlers(); + UI.addSettingsHandlers(); + + // Show the connect panel on first load unless autoconnecting + if (!autoconnect) { + UI.openConnectPanel(); + } + + UI.updateViewClip(); + + UI.updateVisualState(); + + document.getElementById('noVNC_setting_host').focus(); + + var autoconnect = WebUtil.getConfigVar('autoconnect', false); + if (autoconnect === 'true' || autoconnect == '1') { + autoconnect = true; + UI.connect(); + } else { + autoconnect = false; + } + + if (typeof callback === "function") { + callback(UI.rfb); + } + }, + + initSettings: function() { // Stylesheet selection dropdown var sheet = WebUtil.selectStylesheet(); var sheets = WebUtil.getStylesheets(); @@ -114,44 +166,20 @@ var UI; UI.initSetting('path', 'websockify'); UI.initSetting('repeaterID', ''); UI.initSetting('token', ''); + }, - var autoconnect = WebUtil.getConfigVar('autoconnect', false); - if (autoconnect === 'true' || autoconnect == '1') { - autoconnect = true; - UI.connect(); - } else { - autoconnect = false; - } - - UI.updateVisualState(); - - document.getElementById('noVNC_setting_host').focus(); - - // Show mouse selector buttons on touch screen devices - if (UI.isTouchDevice) { - // Show mobile buttons - document.getElementById('noVNC_mobile_buttons').style.display = "inline"; - UI.setMouseButton(); - // Remove the address bar - setTimeout(function() { window.scrollTo(0, 1); }, 100); - UI.forceSetting('clip', true); - } else { - UI.initSetting('clip', false); - } - - UI.setViewClip(); - UI.setBarPosition(); - - window.addEventListener('resize', function () { + setupWindowEvents: function() { + window.addEventListener( 'resize', function () { UI.applyResizeMode(); - UI.setViewClip(); + UI.updateViewClip(); UI.updateViewDrag(); - UI.setBarPosition(); } ); - UI.isSafari = (navigator.userAgent.indexOf('Safari') != -1 && - navigator.userAgent.indexOf('Chrome') == -1); + document.getElementById("noVNC_status") + .addEventListener('click', UI.hideStatus); + }, + setupFullscreen: function() { // Only show the button if fullscreen is properly supported // * Safari doesn't support alphanumerical input while in fullscreen if (!UI.isSafari && @@ -159,44 +187,152 @@ var UI; document.documentElement.mozRequestFullScreen || document.documentElement.webkitRequestFullscreen || document.body.msRequestFullscreen)) { - document.getElementById('noVNC_fullscreen_button').style.display = "inline"; - window.addEventListener('fullscreenchange', UI.updateFullscreenButton); - window.addEventListener('mozfullscreenchange', UI.updateFullscreenButton); - window.addEventListener('webkitfullscreenchange', UI.updateFullscreenButton); - window.addEventListener('msfullscreenchange', UI.updateFullscreenButton); + document.getElementById('noVNC_fullscreen_button') + .classList.remove("noVNC_hidden"); + UI.addFullscreenHandlers(); } + }, + + addControlbarHandlers: function() { + document.getElementById("noVNC_control_bar") + .addEventListener('mousemove', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('mouseup', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('mousedown', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('keypress', UI.activateControlbar); + + document.getElementById("noVNC_control_bar") + .addEventListener('mousedown', UI.keepControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('keypress', UI.keepControlbar); + + document.getElementById("noVNC_view_drag_button") + .addEventListener('click', UI.toggleViewDrag); + + document.getElementById("noVNC_control_bar_handle") + .addEventListener('mousedown', UI.controlbarHandleMouseDown); + document.getElementById("noVNC_control_bar_handle") + .addEventListener('mouseup', UI.controlbarHandleMouseUp); + document.getElementById("noVNC_control_bar_handle") + .addEventListener('mousemove', UI.dragControlbarHandle); + // resize events aren't available for elements + window.addEventListener('resize', UI.updateControlbarHandle); + }, + + addTouchSpecificHandlers: function() { + document.getElementById("noVNC_mouse_button0") + .addEventListener('click', function () { UI.setMouseButton(1); }); + document.getElementById("noVNC_mouse_button1") + .addEventListener('click', function () { UI.setMouseButton(2); }); + document.getElementById("noVNC_mouse_button2") + .addEventListener('click', function () { UI.setMouseButton(4); }); + document.getElementById("noVNC_mouse_button4") + .addEventListener('click', function () { UI.setMouseButton(0); }); + document.getElementById("noVNC_keyboard_button") + .addEventListener('click', UI.toggleVirtualKeyboard); + + document.getElementById("noVNC_keyboardinput") + .addEventListener('input', UI.keyInput); + document.getElementById("noVNC_keyboardinput") + .addEventListener('blur', UI.onblurVirtualKeyboard); + document.getElementById("noVNC_keyboardinput") + .addEventListener('submit', function () { return false; }); + + document.getElementById("noVNC_control_bar") + .addEventListener('touchstart', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('touchmove', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('touchend', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('input', UI.activateControlbar); + + document.getElementById("noVNC_control_bar") + .addEventListener('touchstart', UI.keepControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('input', UI.keepControlbar); + + document.getElementById("noVNC_control_bar_handle") + .addEventListener('touchstart', UI.controlbarHandleMouseDown); + document.getElementById("noVNC_control_bar_handle") + .addEventListener('touchend', UI.controlbarHandleMouseUp); + document.getElementById("noVNC_control_bar_handle") + .addEventListener('touchmove', UI.dragControlbarHandle); window.addEventListener('load', UI.keyboardinputReset); + }, - // While connected we want to display a confirmation dialogue - // if the user tries to leave the page - window.addEventListener('beforeunload', function (e) { - if (UI.rfb && UI.rfb_state === 'normal') { - var msg = "You are currently connected."; - e.returnValue = msg; - return msg; - } else { - return void 0; // To prevent the dialogue when disconnected - } - }); + addExtraKeysHandlers: function() { + document.getElementById("noVNC_toggle_extra_keys_button") + .addEventListener('click', UI.toggleExtraKeys); + document.getElementById("noVNC_toggle_ctrl_button") + .addEventListener('click', UI.toggleCtrl); + document.getElementById("noVNC_toggle_alt_button") + .addEventListener('click', UI.toggleAlt); + document.getElementById("noVNC_send_tab_button") + .addEventListener('click', UI.sendTab); + document.getElementById("noVNC_send_esc_button") + .addEventListener('click', UI.sendEsc); + document.getElementById("noVNC_send_ctrl_alt_del_button") + .addEventListener('click', UI.sendCtrlAltDel); + }, - // Show description by default when hosted at for kanaka.github.com - if (location.host === "kanaka.github.io") { - // Open the description dialog - document.getElementById('noVNC_description').style.display = "block"; - } else { - // Show the connect panel on first load unless autoconnecting - if (autoconnect === UI.connSettingsOpen) { - UI.toggleConnectPanel(); - } - } + addXvpHandlers: function() { + document.getElementById("noVNC_xvp_shutdown_button") + .addEventListener('click', function() { UI.rfb.xvpShutdown(); }); + document.getElementById("noVNC_xvp_reboot_button") + .addEventListener('click', function() { UI.rfb.xvpReboot(); }); + document.getElementById("noVNC_xvp_reset_button") + .addEventListener('click', function() { UI.rfb.xvpReset(); }); + document.getElementById("noVNC_xvp_button") + .addEventListener('click', UI.toggleXvpPanel); + }, - // Add mouse event click/focus/blur event handlers to the UI - UI.addMouseHandlers(); + addConnectionControlHandlers: function() { + document.getElementById("noVNC_connect_controls_button") + .addEventListener('click', UI.toggleConnectPanel); + document.getElementById("noVNC_disconnect_button") + .addEventListener('click', UI.disconnect); + document.getElementById("noVNC_connect_button") + .addEventListener('click', UI.connect); - if (typeof callback === "function") { - callback(UI.rfb); - } + document.getElementById("noVNC_password_button") + .addEventListener('click', UI.setPassword); + }, + + addClipboardHandlers: function() { + document.getElementById("noVNC_clipboard_button") + .addEventListener('click', UI.toggleClipboardPanel); + document.getElementById("noVNC_clipboard_text") + .addEventListener('focus', UI.displayBlur); + document.getElementById("noVNC_clipboard_text") + .addEventListener('blur', UI.displayFocus); + document.getElementById("noVNC_clipboard_text") + .addEventListener('change', UI.clipboardSend); + document.getElementById("noVNC_clipboard_clear_button") + .addEventListener('click', UI.clipboardClear); + }, + + addSettingsHandlers: function() { + document.getElementById("noVNC_settings_button") + .addEventListener('click', UI.toggleSettingsPanel); + document.getElementById("noVNC_settings_apply") + .addEventListener('click', UI.settingsApply); + + document.getElementById("noVNC_setting_resize") + .addEventListener('change', UI.enableDisableViewClip); + }, + + addFullscreenHandlers: function() { + document.getElementById("noVNC_fullscreen_button") + .addEventListener('click', UI.toggleFullscreen); + + window.addEventListener('fullscreenchange', UI.updateFullscreenButton); + window.addEventListener('mozfullscreenchange', UI.updateFullscreenButton); + window.addEventListener('webkitfullscreenchange', UI.updateFullscreenButton); + window.addEventListener('msfullscreenchange', UI.updateFullscreenButton); }, initRFB: function() { @@ -215,53 +351,6 @@ var UI; } }, - addMouseHandlers: function() { - // Setup interface handlers that can't be inline - document.getElementById("noVNC_view_drag_button").onclick = UI.toggleViewDrag; - document.getElementById("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); }; - document.getElementById("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); }; - document.getElementById("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); }; - document.getElementById("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); }; - document.getElementById("noVNC_keyboard_button").onclick = UI.showKeyboard; - - document.getElementById("noVNC_keyboardinput").oninput = UI.keyInput; - document.getElementById("noVNC_keyboardinput").onblur = UI.hideKeyboard; - document.getElementById("noVNC_keyboardinput").onsubmit = function () { return false; }; - - document.getElementById("noVNC_toggleExtraKeys_button").onclick = UI.toggleExtraKeys; - document.getElementById("noVNC_toggleCtrl_button").onclick = UI.toggleCtrl; - document.getElementById("noVNC_toggleAlt_button").onclick = UI.toggleAlt; - document.getElementById("noVNC_sendTab_button").onclick = UI.sendTab; - document.getElementById("noVNC_sendEsc_button").onclick = UI.sendEsc; - - document.getElementById("noVNC_sendCtrlAltDel_button").onclick = UI.sendCtrlAltDel; - document.getElementById("noVNC_xvpShutdown_button").onclick = function() { UI.rfb.xvpShutdown(); }, - document.getElementById("noVNC_xvpReboot_button").onclick = function() { UI.rfb.xvpReboot(); }, - document.getElementById("noVNC_xvpReset_button").onclick = function() { UI.rfb.xvpReset(); }, - document.getElementById("noVNC_status").onclick = UI.popupStatus; - document.getElementById("noVNC_popup_status").onclick = UI.closePopup; - document.getElementById("noVNC_toggleXvp_button").onclick = UI.toggleXvpPanel; - document.getElementById("noVNC_clipboard_button").onclick = UI.toggleClipboardPanel; - document.getElementById("noVNC_fullscreen_button").onclick = UI.toggleFullscreen; - document.getElementById("noVNC_settings_button").onclick = UI.toggleSettingsPanel; - document.getElementById("noVNC_connectPanel_button").onclick = UI.toggleConnectPanel; - document.getElementById("noVNC_disconnect_button").onclick = UI.disconnect; - document.getElementById("noVNC_description_button").onclick = UI.toggleConnectPanel; - - document.getElementById("noVNC_clipboard_text").onfocus = UI.displayBlur; - document.getElementById("noVNC_clipboard_text").onblur = UI.displayFocus; - document.getElementById("noVNC_clipboard_text").onchange = UI.clipboardSend; - document.getElementById("noVNC_clipboard_clear_button").onclick = UI.clipboardClear; - - document.getElementById("noVNC_settings_menu").onmouseover = UI.displayBlur; - document.getElementById("noVNC_settings_menu").onmouseover = UI.displayFocus; - document.getElementById("noVNC_settings_apply").onclick = UI.settingsApply; - - document.getElementById("noVNC_connect_button").onclick = UI.connect; - - document.getElementById("noVNC_setting_resize").onchange = UI.enableDisableViewClip; - }, - /* ------^------- * /INIT * ============== @@ -270,39 +359,33 @@ var UI; updateState: function(rfb, state, oldstate, msg) { UI.rfb_state = state; - var klass; - switch (state) { - case 'failed': - case 'fatal': - klass = "noVNC_status_error"; - break; - case 'normal': - klass = "noVNC_status_normal"; - break; - case 'disconnected': - document.getElementById('noVNC_logo').style.display = "block"; - document.getElementById('noVNC_screen').style.display = "none"; - /* falls through */ - case 'loaded': - klass = "noVNC_status_normal"; - break; - case 'password': - UI.toggleConnectPanel(); - - document.getElementById('noVNC_connect_button').value = "Send Password"; - document.getElementById('noVNC_connect_button').onclick = UI.setPassword; - document.getElementById('noVNC_setting_password').focus(); - - klass = "noVNC_status_warn"; - break; - default: - klass = "noVNC_status_warn"; - break; - } if (typeof(msg) !== 'undefined') { - document.getElementById('noVNC_control_bar').setAttribute("class", klass); - document.getElementById('noVNC_status').innerHTML = msg; + switch (state) { + case 'failed': + case 'fatal': + // zero means no timeout + UI.showStatus(msg, 'error', 0); + break; + case 'normal': + /* falls through */ + case 'disconnected': + case 'loaded': + UI.showStatus(msg, 'normal'); + break; + case 'password': + document.getElementById('noVNC_password_dlg') + .classList.add('noVNC_open'); + setTimeout(function () { + document.getElementById(('noVNC_password_input').focus()); + }, 100); + + UI.showStatus(msg, 'warn'); + break; + default: + UI.showStatus(msg, 'warn'); + break; + } } UI.updateVisualState(); @@ -330,68 +413,213 @@ var UI; document.getElementById('noVNC_setting_repeaterID').disabled = connected; if (connected) { - UI.setViewClip(); + document.documentElement.classList.add("noVNC_connected"); + UI.updateViewClip(); UI.setMouseButton(1); - document.getElementById('noVNC_clipboard_button').style.display = "inline"; - document.getElementById('noVNC_keyboard_button').style.display = "inline"; - document.getElementById('noVNC_extra_keys').style.display = ""; - document.getElementById('noVNC_sendCtrlAltDel_button').style.display = "inline"; + + // Hide the controlbar after 2 seconds + UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000); } else { - UI.setMouseButton(); - document.getElementById('noVNC_clipboard_button').style.display = "none"; - document.getElementById('noVNC_keyboard_button').style.display = "none"; - document.getElementById('noVNC_extra_keys').style.display = "none"; - document.getElementById('noVNC_sendCtrlAltDel_button').style.display = "none"; + document.documentElement.classList.remove("noVNC_connected"); UI.updateXvpButton(0); } // State change disables viewport dragging. // It is enabled (toggled) by direct click on the button - UI.updateViewDrag(false); + UI.setViewDrag(false); + + // State change also closes the password dialog + document.getElementById('noVNC_password_dlg') + .classList.remove('noVNC_open'); switch (UI.rfb_state) { case 'fatal': case 'failed': case 'disconnected': - document.getElementById('noVNC_connectPanel_button').style.display = ""; - document.getElementById('noVNC_disconnect_button').style.display = "none"; - UI.connSettingsOpen = false; - UI.toggleConnectPanel(); + UI.openConnectPanel(); break; case 'loaded': - document.getElementById('noVNC_connectPanel_button').style.display = ""; - document.getElementById('noVNC_disconnect_button').style.display = "none"; break; default: - document.getElementById('noVNC_connectPanel_button').style.display = "none"; - document.getElementById('noVNC_disconnect_button').style.display = ""; break; } //Util.Debug("<< updateVisualState"); }, - popupStatus: function(text) { - var psp = document.getElementById('noVNC_popup_status'); + showStatus: function(text, status_type, time) { + var statusElem = document.getElementById('noVNC_status'); - clearTimeout(UI.popupStatusTimeout); + clearTimeout(UI.statusTimeout); - if (typeof text === 'string') { - psp.innerHTML = text; - } else { - psp.innerHTML = document.getElementById('noVNC_status').innerHTML; + if (typeof status_type === 'undefined') { + status_type = 'normal'; } - psp.style.display = "block"; - psp.style.left = window.innerWidth/2 - - parseInt(window.getComputedStyle(psp).width)/2 -30 + "px"; - // Show the popup for a maximum of 1.5 seconds - UI.popupStatusTimeout = setTimeout(UI.closePopup, 1500); + statusElem.classList.remove("noVNC_status_normal", + "noVNC_status_warn", + "noVNC_status_error"); + + switch (status_type) { + case 'warning': + case 'warn': + statusElem.classList.add("noVNC_status_warn"); + break; + case 'error': + statusElem.classList.add("noVNC_status_error"); + break; + case 'normal': + case 'info': + default: + statusElem.classList.add("noVNC_status_normal"); + break; + } + + statusElem.innerHTML = text; + statusElem.classList.add("noVNC_open"); + + // If no time was specified, show the status for 1.5 seconds + if (typeof time === 'undefined') { + time = 1500; + } + + // A specified time of zero means no timeout + if (time != 0) { + UI.statusTimeout = window.setTimeout(UI.hideStatus, time); + } }, - closePopup: function() { - clearTimeout(UI.popupStatusTimeout); - document.getElementById('noVNC_popup_status').style.display = "none"; + hideStatus: function() { + clearTimeout(UI.statusTimeout); + document.getElementById('noVNC_status').classList.remove("noVNC_open"); + }, + + activateControlbar: function(event) { + clearTimeout(UI.idleControlbarTimeout); + // We manipulate the anchor instead of the actual control + // bar in order to avoid creating new a stacking group + document.getElementById('noVNC_control_bar_anchor') + .classList.remove("noVNC_idle"); + UI.idleControlbarTimeout = window.setTimeout(UI.idleControlbar, 2000); + }, + + idleControlbar: function() { + document.getElementById('noVNC_control_bar_anchor') + .classList.add("noVNC_idle"); + }, + + keepControlbar: function() { + clearTimeout(UI.closeControlbarTimeout); + }, + + openControlbar: function() { + document.getElementById('noVNC_control_bar') + .classList.add("noVNC_open"); + }, + + closeControlbar: function() { + UI.closeAllPanels(); + document.getElementById('noVNC_control_bar') + .classList.remove("noVNC_open"); + }, + + toggleControlbar: function() { + if (document.getElementById('noVNC_control_bar') + .classList.contains("noVNC_open")) { + UI.closeControlbar(); + } else { + UI.openControlbar(); + } + }, + + dragControlbarHandle: function (e) { + if (!UI.controlbarGrabbed) return; + + var ptr = Util.getPointerEvent(e); + + if (!UI.controlbarDrag) { + // The goal is to trigger on a certain physical width, the + // devicePixelRatio brings us a bit closer but is not optimal. + var dragThreshold = 10 * (window.devicePixelRatio || 1); + var dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY); + + if (dragDistance < dragThreshold) return; + + UI.controlbarDrag = true; + } + + var eventY = ptr.clientY - UI.controlbarMouseDownOffsetY; + + UI.moveControlbarHandle(eventY); + + e.preventDefault(); + e.stopPropagation(); + }, + + // Move the handle but don't allow any position outside the bounds + moveControlbarHandle: function (posY) { + var handle = document.getElementById("noVNC_control_bar_handle"); + var handleHeight = Util.getPosition(handle).height; + var controlbar = document.getElementById("noVNC_control_bar"); + var controlbarBounds = Util.getPosition(controlbar); + var controlbarTop = controlbarBounds.y; + var controlbarBottom = controlbarBounds.y + controlbarBounds.height; + var margin = 10; + + var viewportY = posY; + + // Refuse coordinates outside the control bar + if (viewportY < controlbarTop + margin) { + viewportY = controlbarTop + margin; + } else if (viewportY > controlbarBottom - handleHeight - margin) { + viewportY = controlbarBottom - handleHeight - margin; + } + + // Corner case: control bar too small for stable position + if (controlbarBounds.height < (handleHeight + margin * 2)) { + viewportY = controlbarTop + (controlbarBounds.height - handleHeight) / 2; + } + + var relativeY = viewportY - controlbarTop; + handle.style.transform = "translateY(" + relativeY + "px)"; + }, + + updateControlbarHandle: function () { + var handle = document.getElementById("noVNC_control_bar_handle"); + var pos = Util.getPosition(handle); + UI.moveControlbarHandle(pos.y); + }, + + controlbarHandleMouseUp: function(e) { + if ((e.type == "mouseup") && (e.button != 0)) + return; + + // mouseup and mousedown on the same place toggles the controlbar + if (UI.controlbarGrabbed && !UI.controlbarDrag) { + UI.toggleControlbar(); + e.preventDefault(); + e.stopPropagation(); + } + UI.controlbarGrabbed = false; + }, + + controlbarHandleMouseDown: function(e) { + if ((e.type == "mousedown") && (e.button != 0)) + return; + + var ptr = Util.getPointerEvent(e); + + var handle = document.getElementById("noVNC_control_bar_handle"); + var bounds = handle.getBoundingClientRect(); + + WebUtil.setCapture(handle); + UI.controlbarGrabbed = true; + UI.controlbarDrag = false; + + UI.controlbarMouseDownClientY = ptr.clientY; + UI.controlbarMouseDownOffsetY = ptr.clientY - bounds.top; + e.preventDefault(); + e.stopPropagation(); }, /* ------^------- @@ -505,67 +733,75 @@ var UI; // Settings with immediate (non-connected related) effect WebUtil.selectStylesheet(UI.getSetting('stylesheet')); WebUtil.init_logging(UI.getSetting('logging')); - UI.setViewClip(); + UI.updateViewClip(); UI.updateViewDrag(); //Util.Debug("<< settingsApply"); }, - // Open menu - openSettingsMenu: function() { - // Close the description panel - document.getElementById('noVNC_description').style.display = "none"; - // Close clipboard panel if open - if (UI.clipboardOpen === true) { - UI.toggleClipboardPanel(); - } - // Close connection settings if open - if (UI.connSettingsOpen === true) { - UI.toggleConnectPanel(); - } - // Close XVP panel if open - if (UI.xvpOpen === true) { - UI.toggleXvpPanel(); - } - document.getElementById('noVNC_settings').style.display = "block"; - document.getElementById('noVNC_settings_button').className = "noVNC_status_button_selected"; - UI.settingsOpen = true; +/* ------^------- + * /SETTINGS + * ============== + * PANELS + * ------v------*/ + + closeAllPanels: function() { + UI.closeSettingsPanel(); + UI.closeXvpPanel(); + UI.closeClipboardPanel(); + UI.closeConnectPanel(); + UI.closeExtraKeys(); }, - // Close menu (without applying settings) - closeSettingsMenu: function() { - document.getElementById('noVNC_settings').style.display = "none"; - document.getElementById('noVNC_settings_button').className = "noVNC_status_button"; - UI.settingsOpen = false; +/* ------^------- + * /PANELS + * ============== + * SETTINGS (panel) + * ------v------*/ + + openSettingsPanel: function() { + UI.closeAllPanels(); + UI.openControlbar(); + + UI.updateSetting('encrypt'); + UI.updateSetting('true_color'); + if (Util.browserSupportsCursorURIs()) { + UI.updateSetting('cursor'); + } else { + UI.updateSetting('cursor', !UI.isTouchDevice); + document.getElementById('noVNC_setting_cursor').disabled = true; + } + UI.updateSetting('clip'); + UI.updateSetting('resize'); + UI.updateSetting('shared'); + UI.updateSetting('view_only'); + UI.updateSetting('path'); + UI.updateSetting('repeaterID'); + UI.updateSetting('stylesheet'); + UI.updateSetting('logging'); + + document.getElementById('noVNC_settings') + .classList.add("noVNC_open"); + document.getElementById('noVNC_settings_button') + .classList.add("noVNC_selected"); + }, + + closeSettingsPanel: function() { + document.getElementById('noVNC_settings') + .classList.remove("noVNC_open"); + document.getElementById('noVNC_settings_button') + .classList.remove("noVNC_selected"); }, // Toggle the settings menu: // On open, settings are refreshed from saved cookies. // On close, settings are applied toggleSettingsPanel: function() { - // Close the description panel - document.getElementById('noVNC_description').style.display = "none"; - if (UI.settingsOpen) { + if (document.getElementById('noVNC_settings') + .classList.contains("noVNC_open")) { UI.settingsApply(); - UI.closeSettingsMenu(); + UI.closeSettingsPanel(); } else { - UI.updateSetting('encrypt'); - UI.updateSetting('true_color'); - if (Util.browserSupportsCursorURIs()) { - UI.updateSetting('cursor'); - } else { - UI.updateSetting('cursor', !UI.isTouchDevice); - document.getElementById('noVNC_setting_cursor').disabled = true; - } - UI.updateSetting('clip'); - UI.updateSetting('resize'); - UI.updateSetting('shared'); - UI.updateSetting('view_only'); - UI.updateSetting('path'); - UI.updateSetting('repeaterID'); - UI.updateSetting('stylesheet'); - UI.updateSetting('logging'); - - UI.openSettingsMenu(); + UI.openSettingsPanel(); } }, @@ -575,45 +811,42 @@ var UI; * XVP * ------v------*/ - // Show the XVP panel + openXvpPanel: function() { + UI.closeAllPanels(); + UI.openControlbar(); + + document.getElementById('noVNC_xvp') + .classList.add("noVNC_open"); + document.getElementById('noVNC_xvp_button') + .classList.add("noVNC_selected"); + }, + + closeXvpPanel: function() { + document.getElementById('noVNC_xvp') + .classList.remove("noVNC_open"); + document.getElementById('noVNC_xvp_button') + .classList.remove("noVNC_selected"); + }, + toggleXvpPanel: function() { - // Close the description panel - document.getElementById('noVNC_description').style.display = "none"; - // Close settings if open - if (UI.settingsOpen === true) { - UI.settingsApply(); - UI.closeSettingsMenu(); - } - // Close connection settings if open - if (UI.connSettingsOpen === true) { - UI.toggleConnectPanel(); - } - // Close clipboard panel if open - if (UI.clipboardOpen === true) { - UI.toggleClipboardPanel(); - } - // Toggle XVP panel - if (UI.xvpOpen === true) { - document.getElementById('noVNC_xvp').style.display = "none"; - document.getElementById('noVNC_toggleXvp_button').className = "noVNC_status_button"; - UI.xvpOpen = false; + if (document.getElementById('noVNC_xvp') + .classList.contains("noVNC_open")) { + UI.closeXvpPanel(); } else { - document.getElementById('noVNC_xvp').style.display = "block"; - document.getElementById('noVNC_toggleXvp_button').className = "noVNC_status_button_selected"; - UI.xvpOpen = true; + UI.openXvpPanel(); } }, // Disable/enable XVP button updateXvpButton: function(ver) { if (ver >= 1) { - document.getElementById('noVNC_toggleXvp_button').style.display = 'inline'; + document.getElementById('noVNC_xvp_button') + .classList.remove("noVNC_hidden"); } else { - document.getElementById('noVNC_toggleXvp_button').style.display = 'none'; + document.getElementById('noVNC_xvp_button') + .classList.add("noVNC_hidden"); // Close XVP panel if open - if (UI.xvpOpen === true) { - UI.toggleXvpPanel(); - } + UI.closeXvpPanel(); } }, @@ -623,32 +856,29 @@ var UI; * CLIPBOARD * ------v------*/ - // Show the clipboard panel + openClipboardPanel: function() { + UI.closeAllPanels(); + UI.openControlbar(); + + document.getElementById('noVNC_clipboard') + .classList.add("noVNC_open"); + document.getElementById('noVNC_clipboard_button') + .classList.add("noVNC_selected"); + }, + + closeClipboardPanel: function() { + document.getElementById('noVNC_clipboard') + .classList.remove("noVNC_open"); + document.getElementById('noVNC_clipboard_button') + .classList.remove("noVNC_selected"); + }, + toggleClipboardPanel: function() { - // Close the description panel - document.getElementById('noVNC_description').style.display = "none"; - // Close settings if open - if (UI.settingsOpen === true) { - UI.settingsApply(); - UI.closeSettingsMenu(); - } - // Close connection settings if open - if (UI.connSettingsOpen === true) { - UI.toggleConnectPanel(); - } - // Close XVP panel if open - if (UI.xvpOpen === true) { - UI.toggleXvpPanel(); - } - // Toggle Clipboard Panel - if (UI.clipboardOpen === true) { - document.getElementById('noVNC_clipboard').style.display = "none"; - document.getElementById('noVNC_clipboard_button').className = "noVNC_status_button"; - UI.clipboardOpen = false; + if (document.getElementById('noVNC_clipboard') + .classList.contains("noVNC_open")) { + UI.closeClipboardPanel(); } else { - document.getElementById('noVNC_clipboard').style.display = "block"; - document.getElementById('noVNC_clipboard_button').className = "noVNC_status_button_selected"; - UI.clipboardOpen = true; + UI.openClipboardPanel(); } }, @@ -676,45 +906,41 @@ var UI; * CONNECTION * ------v------*/ - // Show the connection settings panel/menu - toggleConnectPanel: function() { - // Close the description panel - document.getElementById('noVNC_description').style.display = "none"; - // Close connection settings if open - if (UI.settingsOpen === true) { - UI.settingsApply(); - UI.closeSettingsMenu(); - document.getElementById('noVNC_connectPanel_button').className = "noVNC_status_button"; - } - // Close clipboard panel if open - if (UI.clipboardOpen === true) { - UI.toggleClipboardPanel(); - } - // Close XVP panel if open - if (UI.xvpOpen === true) { - UI.toggleXvpPanel(); - } + openConnectPanel: function() { + UI.closeAllPanels(); + UI.openControlbar(); - // Toggle Connection Panel - if (UI.connSettingsOpen === true) { - document.getElementById('noVNC_controls').style.display = "none"; - document.getElementById('noVNC_connectPanel_button').className = "noVNC_status_button"; - UI.connSettingsOpen = false; - UI.saveSetting('host'); - UI.saveSetting('port'); - UI.saveSetting('token'); - //UI.saveSetting('password'); + document.getElementById('noVNC_connect_controls') + .classList.add("noVNC_open"); + document.getElementById('noVNC_connect_controls_button') + .classList.add("noVNC_selected"); + + document.getElementById('noVNC_setting_host').focus(); + }, + + closeConnectPanel: function() { + document.getElementById('noVNC_connect_controls') + .classList.remove("noVNC_open"); + document.getElementById('noVNC_connect_controls_button') + .classList.remove("noVNC_selected"); + + UI.saveSetting('host'); + UI.saveSetting('port'); + UI.saveSetting('token'); + //UI.saveSetting('password'); + }, + + toggleConnectPanel: function() { + if (document.getElementById('noVNC_connect_controls') + .classList.contains("noVNC_open")) { + UI.closeConnectPanel(); } else { - document.getElementById('noVNC_controls').style.display = "block"; - document.getElementById('noVNC_connectPanel_button').className = "noVNC_status_button_selected"; - UI.connSettingsOpen = true; - document.getElementById('noVNC_setting_host').focus(); + UI.openConnectPanel(); } }, connect: function() { - UI.closeSettingsMenu(); - UI.toggleConnectPanel(); + UI.closeAllPanels(); var host = document.getElementById('noVNC_setting_host').value; var port = document.getElementById('noVNC_setting_port').value; @@ -741,33 +967,22 @@ var UI; UI.rfb.set_repeaterID(UI.getSetting('repeaterID')); UI.rfb.connect(host, port, password, path); - - //Close dialog. - setTimeout(UI.setBarPosition, 100); - document.getElementById('noVNC_logo').style.display = "none"; - document.getElementById('noVNC_screen').style.display = "inline"; }, disconnect: function() { - UI.closeSettingsMenu(); + UI.closeAllPanels(); UI.rfb.disconnect(); // Restore the callback used for initial resize UI.rfb.set_onFBUComplete(UI.initialResize); - document.getElementById('noVNC_logo').style.display = "block"; - document.getElementById('noVNC_screen').style.display = "none"; - // Don't display the connection settings until we're actually disconnected }, setPassword: function() { - UI.rfb.sendPassword(document.getElementById('noVNC_setting_password').value); - //Reset connect button. - document.getElementById('noVNC_connect_button').value = "Connect"; - document.getElementById('noVNC_connect_button').onclick = UI.connect; - //Hide connection panel. - UI.toggleConnectPanel(); + UI.rfb.sendPassword(document.getElementById('noVNC_password_input').value); + document.getElementById('noVNC_password_dlg') + .classList.remove('noVNC_open'); return false; }, @@ -811,9 +1026,11 @@ var UI; document.mozFullScreenElement || // currently working methods document.webkitFullscreenElement || document.msFullscreenElement ) { - document.getElementById('noVNC_fullscreen_button').className = "noVNC_status_button_selected"; + document.getElementById('noVNC_fullscreen_button') + .classList.add("noVNC_selected"); } else { - document.getElementById('noVNC_fullscreen_button').className = "noVNC_status_button"; + document.getElementById('noVNC_fullscreen_button') + .classList.remove("noVNC_selected"); } }, @@ -905,54 +1122,52 @@ var UI; // Set and configure viewport clipping setViewClip: function(clip) { + UI.updateSetting('clip', clip); + UI.updateViewClip(); + }, + + // Update parameters that depend on the clip setting + updateViewClip: function() { var display; - if (UI.rfb) { - display = UI.rfb.get_display(); - } else { - UI.forceSetting('clip', clip); + if (!UI.rfb) { return; } + var display = UI.rfb.get_display(); var cur_clip = display.get_viewport(); + var new_clip = UI.getSetting('clip'); - if (typeof(clip) !== 'boolean') { - // Use current setting - clip = UI.getSetting('clip'); + if (cur_clip !== new_clip) { + display.set_viewport(new_clip); } - if (clip && !cur_clip) { - // Turn clipping on - UI.updateSetting('clip', true); - } else if (!clip && cur_clip) { - // Turn clipping off - UI.updateSetting('clip', false); - display.set_viewport(false); + var size = UI.screenSize(); + + if (new_clip && size) { + // When clipping is enabled, the screen is limited to + // the size of the browser window. + display.set_maxWidth(size.w); + display.set_maxHeight(size.h); + + var screen = document.getElementById('noVNC_screen'); + var canvas = document.getElementById('noVNC_canvas'); + + // Hide potential scrollbars that can skew the position + screen.style.overflow = "hidden"; + + // The x position marks the left margin of the canvas, + // remove the margin from both sides to keep it centered. + var new_w = size.w - (2 * Util.getPosition(canvas).x); + + screen.style.overflow = "visible"; + + display.viewportChangeSize(new_w, size.h); + } else { // Disable max dimensions display.set_maxWidth(0); display.set_maxHeight(0); display.viewportChangeSize(); } - if (UI.getSetting('clip')) { - // If clipping, update clipping settings - display.set_viewport(true); - - var size = UI.screenSize(); - if (size) { - display.set_maxWidth(size.w); - display.set_maxHeight(size.h); - - // Hide potential scrollbars that can skew the position - document.getElementById('noVNC_screen').style.overflow = "hidden"; - - // The x position marks the left margin of the canvas, - // remove the margin from both sides to keep it centered - var new_w = size.w - (2 * Util.getPosition(document.getElementById('noVNC_canvas')).x); - - document.getElementById('noVNC_screen').style.overflow = "visible"; - - display.viewportChangeSize(new_w, size.h); - } - } }, // Handle special cases where clipping is forced on/off or locked @@ -973,7 +1188,7 @@ var UI; // The browser is IE and we are in fullscreen mode. // - We need to force clipping while in fullscreen since // scrollbars doesn't work. - UI.popupStatus("Forcing clipping mode since scrollbars aren't supported by IE in fullscreen"); + UI.showStatus("Forcing clipping mode since scrollbars aren't supported by IE in fullscreen"); UI.rememberedClipSetting = UI.getSetting('clip'); UI.setViewClip(true); document.getElementById('noVNC_setting_clip').disabled = true; @@ -995,62 +1210,68 @@ var UI; * VIEWDRAG * ------v------*/ - // Update the viewport drag state - updateViewDrag: function(drag) { - if (!UI.rfb) return; - - var viewDragButton = document.getElementById('noVNC_view_drag_button'); - - // Check if viewport drag is possible. It is only possible - // if the remote display is clipping the client display. - if (UI.rfb_state === 'normal' && - UI.rfb.get_display().get_viewport() && - UI.rfb.get_display().clippingDisplay()) { - - viewDragButton.style.display = "inline"; - viewDragButton.disabled = false; - - } else { - // The size of the remote display is the same or smaller - // than the client display. Make sure viewport drag isn't - // active when it can't be used. - if (UI.rfb.get_viewportDrag) { - viewDragButton.className = "noVNC_status_button"; - UI.rfb.set_viewportDrag(false); - } - - // The button is disabled instead of hidden on touch devices - if (UI.rfb_state === 'normal' && UI.isTouchDevice) { - viewDragButton.style.display = "inline"; - viewDragButton.disabled = true; - } else { - viewDragButton.style.display = "none"; - } - return; - } - - if (typeof(drag) !== "undefined" && - typeof(drag) !== "object") { - if (drag) { - viewDragButton.className = "noVNC_status_button_selected"; - UI.rfb.set_viewportDrag(true); - } else { - viewDragButton.className = "noVNC_status_button"; - UI.rfb.set_viewportDrag(false); - } - } - }, - toggleViewDrag: function() { if (!UI.rfb) return; + var drag = UI.rfb.get_viewportDrag(); + UI.setViewDrag(!drag); + }, + + // Set the view drag mode which moves the viewport on mouse drags + setViewDrag: function(drag) { + if (!UI.rfb) return; + + UI.rfb.set_viewportDrag(drag); + + UI.updateViewDrag(); + }, + + updateViewDrag: function() { + var clipping = false; + + if (UI.rfb_state !== 'normal') return; + + // Check if viewport drag is possible. It is only possible + // if the remote display is clipping the client display. + if (UI.rfb.get_display().get_viewport() && + UI.rfb.get_display().clippingDisplay()) { + clipping = true; + } + var viewDragButton = document.getElementById('noVNC_view_drag_button'); - if (UI.rfb.get_viewportDrag()) { - viewDragButton.className = "noVNC_status_button"; + + if (!clipping && + UI.rfb.get_viewportDrag()) { + // The size of the remote display is the same or smaller + // than the client display. Make sure viewport drag isn't + // active when it can't be used. UI.rfb.set_viewportDrag(false); + } + + if (UI.rfb.get_viewportDrag()) { + viewDragButton.classList.add("noVNC_selected"); } else { - viewDragButton.className = "noVNC_status_button_selected"; - UI.rfb.set_viewportDrag(true); + viewDragButton.classList.remove("noVNC_selected"); + } + + // Different behaviour for touch vs non-touch + // The button is disabled instead of hidden on touch devices + if (UI.isTouchDevice) { + viewDragButton.classList.remove("noVNC_hidden"); + + if (clipping) { + viewDragButton.disabled = false; + } else { + viewDragButton.disabled = true; + } + } else { + viewDragButton.disabled = false; + + if (clipping) { + viewDragButton.classList.remove("noVNC_hidden"); + } else { + viewDragButton.classList.add("noVNC_hidden"); + } } }, @@ -1060,42 +1281,64 @@ var UI; * KEYBOARD * ------v------*/ - // On touch devices, show the OS keyboard - showKeyboard: function() { - var kbi = document.getElementById('noVNC_keyboardinput'); - var skb = document.getElementById('noVNC_keyboard_button'); - var l = kbi.value.length; - if(UI.keyboardVisible === false) { - kbi.focus(); - try { kbi.setSelectionRange(l, l); } // Move the caret to the end - catch (err) {} // setSelectionRange is undefined in Google Chrome - UI.keyboardVisible = true; - skb.className = "noVNC_status_button_selected"; - } else if(UI.keyboardVisible === true) { - kbi.blur(); - skb.className = "noVNC_status_button"; - UI.keyboardVisible = false; + showVirtualKeyboard: function() { + if (!UI.isTouchDevice) + return; + + var input = document.getElementById('noVNC_keyboardinput'); + + if (document.activeElement == input) + return; + + UI.keyboardVisible = true; + document.getElementById('noVNC_keyboard_button') + .classList.add("noVNC_selected"); + input.focus(); + + try { + var l = input.value.length; + // Move the caret to the end + input.setSelectionRange(l, l); + } catch (err) {} // setSelectionRange is undefined in Google Chrome + }, + + hideVirtualKeyboard: function() { + if (!UI.isTouchDevice) + return; + + var input = document.getElementById('noVNC_keyboardinput'); + + if (document.activeElement != input) + return; + + input.blur(); + }, + + toggleVirtualKeyboard: function () { + if (UI.keyboardVisible) { + UI.hideVirtualKeyboard(); + } else { + UI.showVirtualKeyboard(); } }, - hideKeyboard: function() { - document.getElementById('noVNC_keyboard_button').className = "noVNC_status_button"; + onblurVirtualKeyboard: function() { //Weird bug in iOS if you change keyboardVisible //here it does not actually occur so next time //you click keyboard icon it doesnt work. UI.hideKeyboardTimeout = setTimeout(function() { UI.keyboardVisible = false; + document.getElementById('noVNC_keyboard_button') + .classList.remove("noVNC_selected"); },100); }, keepKeyboard: function() { clearTimeout(UI.hideKeyboardTimeout); if(UI.keyboardVisible === true) { - document.getElementById('noVNC_keyboardinput').focus(); - document.getElementById('noVNC_keyboard_button').className = "noVNC_status_button_selected"; + UI.showVirtualKeyboard(); } else if(UI.keyboardVisible === false) { - document.getElementById('noVNC_keyboardinput').blur(); - document.getElementById('noVNC_keyboard_button').className = "noVNC_status_button"; + UI.hideVirtualKeyboard(); } }, @@ -1176,22 +1419,36 @@ var UI; } }, +/* ------^------- + * /KEYBOARD + * ============== + * EXTRA KEYS + * ------v------*/ + + openExtraKeys: function() { + UI.closeAllPanels(); + UI.openControlbar(); + + document.getElementById('noVNC_modifiers') + .classList.add("noVNC_open"); + document.getElementById('noVNC_toggle_extra_keys_button') + .classList.add("noVNC_selected"); + }, + + closeExtraKeys: function() { + document.getElementById('noVNC_modifiers') + .classList.remove("noVNC_open"); + document.getElementById('noVNC_toggle_extra_keys_button') + .classList.remove("noVNC_selected"); + }, + toggleExtraKeys: function() { UI.keepKeyboard(); - if(UI.extraKeysVisible === false) { - document.getElementById('noVNC_toggleCtrl_button').style.display = "inline"; - document.getElementById('noVNC_toggleAlt_button').style.display = "inline"; - document.getElementById('noVNC_sendTab_button').style.display = "inline"; - document.getElementById('noVNC_sendEsc_button').style.display = "inline"; - document.getElementById('noVNC_toggleExtraKeys_button').className = "noVNC_status_button_selected"; - UI.extraKeysVisible = true; - } else if(UI.extraKeysVisible === true) { - document.getElementById('noVNC_toggleCtrl_button').style.display = ""; - document.getElementById('noVNC_toggleAlt_button').style.display = ""; - document.getElementById('noVNC_sendTab_button').style.display = ""; - document.getElementById('noVNC_sendEsc_button').style.display = ""; - document.getElementById('noVNC_toggleExtraKeys_button').className = "noVNC_status_button"; - UI.extraKeysVisible = false; + if(document.getElementById('noVNC_modifiers') + .classList.contains("noVNC_open")) { + UI.closeExtraKeys(); + } else { + UI.openExtraKeys(); } }, @@ -1207,45 +1464,40 @@ var UI; toggleCtrl: function() { UI.keepKeyboard(); - if(UI.ctrlOn === false) { - UI.rfb.sendKey(KeyTable.XK_Control_L, true); - document.getElementById('noVNC_toggleCtrl_button').className = "noVNC_status_button_selected"; - UI.ctrlOn = true; - } else if(UI.ctrlOn === true) { + var btn = document.getElementById('noVNC_toggle_ctrl_button'); + if (btn.classList.contains("noVNC_selected")) { UI.rfb.sendKey(KeyTable.XK_Control_L, false); - document.getElementById('noVNC_toggleCtrl_button').className = "noVNC_status_button"; - UI.ctrlOn = false; + btn.classList.remove("noVNC_selected"); + } else { + UI.rfb.sendKey(KeyTable.XK_Control_L, true); + btn.classList.add("noVNC_selected"); } }, toggleAlt: function() { UI.keepKeyboard(); - if(UI.altOn === false) { - UI.rfb.sendKey(KeyTable.XK_Alt_L, true); - document.getElementById('noVNC_toggleAlt_button').className = "noVNC_status_button_selected"; - UI.altOn = true; - } else if(UI.altOn === true) { + var btn = document.getElementById('noVNC_toggle_alt_button'); + if (btn.classList.contains("noVNC_selected")) { UI.rfb.sendKey(KeyTable.XK_Alt_L, false); - document.getElementById('noVNC_toggleAlt_button').className = "noVNC_status_button"; - UI.altOn = false; + btn.classList.remove("noVNC_selected"); + } else { + UI.rfb.sendKey(KeyTable.XK_Alt_L, true); + btn.classList.add("noVNC_selected"); } }, sendCtrlAltDel: function() { + UI.keepKeyboard(); UI.rfb.sendCtrlAltDel(); }, /* ------^------- - * /KEYBOARD + * /EXTRA KEYS * ============== * MISC * ------v------*/ setMouseButton: function(num) { - if (typeof num === 'undefined') { - // Disable mouse buttons - num = -1; - } if (UI.rfb) { UI.rfb.get_mouse().set_touchButton(num); } @@ -1254,9 +1506,9 @@ var UI; for (var b = 0; b < blist.length; b++) { var button = document.getElementById('noVNC_mouse_button' + blist[b]); if (blist[b] === num) { - button.style.display = ""; + button.classList.remove("noVNC_hidden"); } else { - button.style.display = "none"; + button.classList.add("noVNC_hidden"); } } }, @@ -1288,14 +1540,6 @@ var UI; selectbox.options.add(optn); }, - setBarPosition: function() { - document.getElementById('noVNC_control_bar').style.top = (window.pageYOffset) + 'px'; - document.getElementById('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px'; - - var vncwidth = document.getElementById('noVNC_container').style.offsetWidth; - document.getElementById('noVNC_control_bar').style.width = vncwidth + 'px'; - } - /* ------^------- * /MISC * ============== diff --git a/app/webutil.js b/app/webutil.js index 7f234dbf..38bb3967 100644 --- a/app/webutil.js +++ b/app/webutil.js @@ -278,6 +278,99 @@ WebUtil.injectParamIfMissing = function (path, param, value) { } }; +// Emulate Element.setCapture() when not supported + +WebUtil._captureRecursion = false; +WebUtil._captureProxy = function (e) { + // Recursion protection as we'll see our own event + if (WebUtil._captureRecursion) return; + + // Clone the event as we cannot dispatch an already dispatched event + var newEv = new e.constructor(e.type, e); + + WebUtil._captureRecursion = true; + WebUtil._captureElem.dispatchEvent(newEv); + WebUtil._captureRecursion = false; + + // Implicitly release the capture on button release + if ((e.type === "mouseup") || (e.type === "touchend")) { + WebUtil.releaseCapture(); + } +}; + +WebUtil.setCapture = function (elem) { + if (elem.setCapture) { + + elem.setCapture(); + + // IE releases capture on 'click' events which might not trigger + elem.addEventListener('mouseup', WebUtil.releaseCapture); + elem.addEventListener('touchend', WebUtil.releaseCapture); + + } else { + // Safari on iOS 9 has a broken constructor for TouchEvent. + // We are fine in this case however, since Safari seems to + // have some sort of implicit setCapture magic anyway. + if (window.TouchEvent !== undefined) { + try { + new TouchEvent("touchstart"); + } catch (TypeError) { + return; + } + } + + var captureElem = document.getElementById("noVNC_mouse_capture_elem"); + + if (captureElem === null) { + captureElem = document.createElement("div"); + captureElem.id = "noVNC_mouse_capture_elem"; + captureElem.style.position = "fixed"; + captureElem.style.top = "0px"; + captureElem.style.left = "0px"; + captureElem.style.width = "100%"; + captureElem.style.height = "100%"; + captureElem.style.zIndex = 10000; + captureElem.style.display = "none"; + document.body.appendChild(captureElem); + + captureElem.addEventListener('mousemove', WebUtil._captureProxy); + captureElem.addEventListener('mouseup', WebUtil._captureProxy); + + captureElem.addEventListener('touchmove', WebUtil._captureProxy); + captureElem.addEventListener('touchend', WebUtil._captureProxy); + } + + WebUtil._captureElem = elem; + captureElem.style.display = null; + + // We listen to events on window in order to keep tracking if it + // happens to leave the viewport + window.addEventListener('mousemove', WebUtil._captureProxy); + window.addEventListener('mouseup', WebUtil._captureProxy); + + window.addEventListener('touchmove', WebUtil._captureProxy); + window.addEventListener('touchend', WebUtil._captureProxy); + } +}; + +WebUtil.releaseCapture = function () { + if (document.releaseCapture) { + + document.releaseCapture(); + + } else { + var captureElem = document.getElementById("noVNC_mouse_capture_elem"); + WebUtil._captureElem = null; + captureElem.style.display = "none"; + + window.removeEventListener('mousemove', WebUtil._captureProxy); + window.removeEventListener('mouseup', WebUtil._captureProxy); + + window.removeEventListener('touchmove', WebUtil._captureProxy); + window.removeEventListener('touchend', WebUtil._captureProxy); + } +}; + // Dynamically load scripts without using document.write() // Reference: http://unixpapa.com/js/dyna.html // diff --git a/core/util.js b/core/util.js index d6a01932..3cc1e876 100644 --- a/core/util.js +++ b/core/util.js @@ -204,14 +204,18 @@ Util.getPosition = function(obj) { 'width': objPosition.width, 'height': objPosition.height}; }; +Util.getPointerEvent = function (e) { + var evt; + evt = (e ? e : window.event); + evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt); + return evt; +}; // Get mouse event position in DOM element Util.getEventPosition = function (e, obj, scale) { "use strict"; var evt, docX, docY, pos; - //if (!e) evt = window.event; - evt = (e ? e : window.event); - evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt); + evt = Util.getPointerEvent(e); if (evt.pageX || evt.pageY) { docX = evt.pageX; docY = evt.pageY; diff --git a/vnc.html b/vnc.html index 8f4129dc..d6348e5c 100644 --- a/vnc.html +++ b/vnc.html @@ -6,6 +6,7 @@ noVNC example: simple example using default UI Copyright (C) 2012 Joel Martin Copyright (C) 2016 Samuel Mannehed for Cendio AB + Copyright (C) 2016 Pierre Ossman for Cendio AB noVNC is licensed under the MPL 2.0 (see LICENSE.txt) This file is licensed under the 2-Clause BSD license (see LICENSE.txt). @@ -48,169 +49,245 @@
-