@jait/gateway 0.1.256 → 0.1.258

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/dist/routes/preview.d.ts.map +1 -1
  2. package/dist/routes/preview.js +41 -2
  3. package/dist/routes/preview.js.map +1 -1
  4. package/dist/security/sandbox-manager.d.ts +8 -0
  5. package/dist/security/sandbox-manager.d.ts.map +1 -1
  6. package/dist/security/sandbox-manager.js +131 -2
  7. package/dist/security/sandbox-manager.js.map +1 -1
  8. package/dist/services/live-view-manager.d.ts +25 -0
  9. package/dist/services/live-view-manager.d.ts.map +1 -0
  10. package/dist/services/live-view-manager.js +178 -0
  11. package/dist/services/live-view-manager.js.map +1 -0
  12. package/dist/services/preview.d.ts +21 -2
  13. package/dist/services/preview.d.ts.map +1 -1
  14. package/dist/services/preview.js +140 -2
  15. package/dist/services/preview.js.map +1 -1
  16. package/dist/surfaces/browser.d.ts +51 -1
  17. package/dist/surfaces/browser.d.ts.map +1 -1
  18. package/dist/surfaces/browser.js +238 -20
  19. package/dist/surfaces/browser.js.map +1 -1
  20. package/dist/tools/preview-tools.d.ts.map +1 -1
  21. package/dist/tools/preview-tools.js +6 -3
  22. package/dist/tools/preview-tools.js.map +1 -1
  23. package/package.json +1 -1
  24. package/web-dist/assets/{_basePickBy-B5ojxQyY.js → _basePickBy-B1Q85huU.js} +1 -1
  25. package/web-dist/assets/{_baseUniq-BVcMC3e3.js → _baseUniq-xbSl0CM3.js} +1 -1
  26. package/web-dist/assets/{arc-B8rKyks5.js → arc-BPQiaftr.js} +1 -1
  27. package/web-dist/assets/{architectureDiagram-2XIMDMQ5-D5Rn0_M_.js → architectureDiagram-2XIMDMQ5-DAG0yXkD.js} +1 -1
  28. package/web-dist/assets/{blockDiagram-WCTKOSBZ-Dn0EedHT.js → blockDiagram-WCTKOSBZ-BHwTxFpO.js} +1 -1
  29. package/web-dist/assets/{c4Diagram-IC4MRINW-BYTFS9YT.js → c4Diagram-IC4MRINW-DSo8jOax.js} +1 -1
  30. package/web-dist/assets/channel-D0UG72Sd.js +1 -0
  31. package/web-dist/assets/{chunk-4BX2VUAB-c51Qw6zZ.js → chunk-4BX2VUAB-BvHzmUEU.js} +1 -1
  32. package/web-dist/assets/{chunk-55IACEB6-BLJEbYZY.js → chunk-55IACEB6-DRwUUfw9.js} +1 -1
  33. package/web-dist/assets/{chunk-FMBD7UC4-BTQRsQCz.js → chunk-FMBD7UC4-C-og1AV4.js} +1 -1
  34. package/web-dist/assets/{chunk-JSJVCQXG-BCQCgV3v.js → chunk-JSJVCQXG-DQ_Nx-12.js} +1 -1
  35. package/web-dist/assets/{chunk-KX2RTZJC-BdxNziNu.js → chunk-KX2RTZJC-DJt22pKX.js} +1 -1
  36. package/web-dist/assets/{chunk-NQ4KR5QH-DvtJCn0C.js → chunk-NQ4KR5QH-DwqccJna.js} +1 -1
  37. package/web-dist/assets/{chunk-QZHKN3VN-Bv5UUhJr.js → chunk-QZHKN3VN-t2Z5FHem.js} +1 -1
  38. package/web-dist/assets/{chunk-WL4C6EOR-DgCZzsOT.js → chunk-WL4C6EOR-DhnNywRj.js} +1 -1
  39. package/web-dist/assets/classDiagram-VBA2DB6C-Dnib5gxK.js +1 -0
  40. package/web-dist/assets/classDiagram-v2-RAHNMMFH-Dnib5gxK.js +1 -0
  41. package/web-dist/assets/clone-jMsfbHCu.js +1 -0
  42. package/web-dist/assets/{cose-bilkent-S5V4N54A-CmDdGIef.js → cose-bilkent-S5V4N54A-CLw8R8jY.js} +1 -1
  43. package/web-dist/assets/{dagre-KLK3FWXG-BzeF6p1D.js → dagre-KLK3FWXG-DG-8BOX_.js} +1 -1
  44. package/web-dist/assets/{diagram-E7M64L7V-lDeWArgl.js → diagram-E7M64L7V-I6Ok2sG0.js} +1 -1
  45. package/web-dist/assets/{diagram-IFDJBPK2-BguEZbUg.js → diagram-IFDJBPK2-ertAk1QG.js} +1 -1
  46. package/web-dist/assets/{diagram-P4PSJMXO-CnTxg2wa.js → diagram-P4PSJMXO-B9Yr9iab.js} +1 -1
  47. package/web-dist/assets/{erDiagram-INFDFZHY-D2aGee-H.js → erDiagram-INFDFZHY-BMva8gYu.js} +1 -1
  48. package/web-dist/assets/{flowDiagram-PKNHOUZH-DYPu_lOt.js → flowDiagram-PKNHOUZH-D1dJMWaL.js} +1 -1
  49. package/web-dist/assets/{ganttDiagram-A5KZAMGK-D-9aApiK.js → ganttDiagram-A5KZAMGK-qRDi6l_-.js} +1 -1
  50. package/web-dist/assets/{gitGraphDiagram-K3NZZRJ6-CGHZTbqZ.js → gitGraphDiagram-K3NZZRJ6-X8idci3H.js} +1 -1
  51. package/web-dist/assets/{graph-xl1n4wd4.js → graph-6RqPLKMD.js} +1 -1
  52. package/web-dist/assets/{index-CNdW3yYd.js → index-B0GgFn4l.js} +306 -306
  53. package/web-dist/assets/{index-qWyAYgAD.css → index-DFW3_r8A.css} +1 -1
  54. package/web-dist/assets/{infoDiagram-LFFYTUFH-JQ1DhuIN.js → infoDiagram-LFFYTUFH-CMk_Yj9c.js} +1 -1
  55. package/web-dist/assets/{ishikawaDiagram-PHBUUO56-BiQo9puY.js → ishikawaDiagram-PHBUUO56-CwBIr6E2.js} +1 -1
  56. package/web-dist/assets/{journeyDiagram-4ABVD52K-BWvVCFKb.js → journeyDiagram-4ABVD52K-BgD5iS1h.js} +1 -1
  57. package/web-dist/assets/{kanban-definition-K7BYSVSG-B1pZvgm7.js → kanban-definition-K7BYSVSG-CzjxHzou.js} +1 -1
  58. package/web-dist/assets/{layout-C_VnOnT4.js → layout-b5fe_UxD.js} +1 -1
  59. package/web-dist/assets/{linear-BeBoVpvX.js → linear-ChI_4a9O.js} +1 -1
  60. package/web-dist/assets/{mindmap-definition-YRQLILUH-B3U5ZJAw.js → mindmap-definition-YRQLILUH-e_vvWWg8.js} +1 -1
  61. package/web-dist/assets/{pieDiagram-SKSYHLDU-B9JMxGDx.js → pieDiagram-SKSYHLDU-CJtQ5J0D.js} +1 -1
  62. package/web-dist/assets/{quadrantDiagram-337W2JSQ-DNAEOevf.js → quadrantDiagram-337W2JSQ-Cf_x_2ej.js} +1 -1
  63. package/web-dist/assets/{requirementDiagram-Z7DCOOCP-Dcgzabvl.js → requirementDiagram-Z7DCOOCP-DCQvoqk_.js} +1 -1
  64. package/web-dist/assets/{sankeyDiagram-WA2Y5GQK-BDcQVCtL.js → sankeyDiagram-WA2Y5GQK-D2eB9_Im.js} +1 -1
  65. package/web-dist/assets/{sequenceDiagram-2WXFIKYE-5kY7GmoR.js → sequenceDiagram-2WXFIKYE-Ccyyepsb.js} +1 -1
  66. package/web-dist/assets/{stateDiagram-RAJIS63D-BwQlMlEC.js → stateDiagram-RAJIS63D-DPMGShy8.js} +1 -1
  67. package/web-dist/assets/stateDiagram-v2-FVOUBMTO-BmvKM7hT.js +1 -0
  68. package/web-dist/assets/{timeline-definition-YZTLITO2-BE0IDpJe.js → timeline-definition-YZTLITO2-CHBPr8N4.js} +1 -1
  69. package/web-dist/assets/{treemap-KZPCXAKY-CHwtWAgO.js → treemap-KZPCXAKY-DnqeIQgk.js} +1 -1
  70. package/web-dist/assets/{vennDiagram-LZ73GAT5-ByqFK92_.js → vennDiagram-LZ73GAT5-BRIzanhU.js} +1 -1
  71. package/web-dist/assets/{xychartDiagram-JWTSCODW-BdCa1f3c.js → xychartDiagram-JWTSCODW-qjjqxrk2.js} +1 -1
  72. package/web-dist/index.html +2 -2
  73. package/web-dist/noVNC/LICENSE.txt +62 -0
  74. package/web-dist/noVNC/app/error-handler.js +79 -0
  75. package/web-dist/noVNC/app/images/alt.svg +92 -0
  76. package/web-dist/noVNC/app/images/clipboard.svg +106 -0
  77. package/web-dist/noVNC/app/images/connect.svg +96 -0
  78. package/web-dist/noVNC/app/images/ctrl.svg +96 -0
  79. package/web-dist/noVNC/app/images/ctrlaltdel.svg +100 -0
  80. package/web-dist/noVNC/app/images/disconnect.svg +94 -0
  81. package/web-dist/noVNC/app/images/drag.svg +76 -0
  82. package/web-dist/noVNC/app/images/error.svg +81 -0
  83. package/web-dist/noVNC/app/images/esc.svg +92 -0
  84. package/web-dist/noVNC/app/images/expander.svg +69 -0
  85. package/web-dist/noVNC/app/images/fullscreen.svg +93 -0
  86. package/web-dist/noVNC/app/images/handle.svg +82 -0
  87. package/web-dist/noVNC/app/images/handle_bg.svg +172 -0
  88. package/web-dist/noVNC/app/images/icons/Makefile +42 -0
  89. package/web-dist/noVNC/app/images/icons/novnc-icon-sm.svg +163 -0
  90. package/web-dist/noVNC/app/images/icons/novnc-icon.svg +163 -0
  91. package/web-dist/noVNC/app/images/icons/novnc-ios-120.png +0 -0
  92. package/web-dist/noVNC/app/images/icons/novnc-ios-152.png +0 -0
  93. package/web-dist/noVNC/app/images/icons/novnc-ios-167.png +0 -0
  94. package/web-dist/noVNC/app/images/icons/novnc-ios-180.png +0 -0
  95. package/web-dist/noVNC/app/images/icons/novnc-ios-40.png +0 -0
  96. package/web-dist/noVNC/app/images/icons/novnc-ios-58.png +0 -0
  97. package/web-dist/noVNC/app/images/icons/novnc-ios-60.png +0 -0
  98. package/web-dist/noVNC/app/images/icons/novnc-ios-80.png +0 -0
  99. package/web-dist/noVNC/app/images/icons/novnc-ios-87.png +0 -0
  100. package/web-dist/noVNC/app/images/icons/novnc-ios-icon.svg +183 -0
  101. package/web-dist/noVNC/app/images/icons/novnc.ico +0 -0
  102. package/web-dist/noVNC/app/images/info.svg +81 -0
  103. package/web-dist/noVNC/app/images/keyboard.svg +88 -0
  104. package/web-dist/noVNC/app/images/power.svg +87 -0
  105. package/web-dist/noVNC/app/images/settings.svg +76 -0
  106. package/web-dist/noVNC/app/images/tab.svg +86 -0
  107. package/web-dist/noVNC/app/images/toggleextrakeys.svg +90 -0
  108. package/web-dist/noVNC/app/images/warning.svg +81 -0
  109. package/web-dist/noVNC/app/images/windows.svg +65 -0
  110. package/web-dist/noVNC/app/locale/README +1 -0
  111. package/web-dist/noVNC/app/locale/cs.json +71 -0
  112. package/web-dist/noVNC/app/locale/de.json +74 -0
  113. package/web-dist/noVNC/app/locale/el.json +100 -0
  114. package/web-dist/noVNC/app/locale/es.json +68 -0
  115. package/web-dist/noVNC/app/locale/fr.json +82 -0
  116. package/web-dist/noVNC/app/locale/hu.json +80 -0
  117. package/web-dist/noVNC/app/locale/it.json +68 -0
  118. package/web-dist/noVNC/app/locale/ja.json +81 -0
  119. package/web-dist/noVNC/app/locale/ko.json +70 -0
  120. package/web-dist/noVNC/app/locale/nl.json +95 -0
  121. package/web-dist/noVNC/app/locale/pl.json +80 -0
  122. package/web-dist/noVNC/app/locale/pt_BR.json +72 -0
  123. package/web-dist/noVNC/app/locale/ru.json +72 -0
  124. package/web-dist/noVNC/app/locale/sv.json +83 -0
  125. package/web-dist/noVNC/app/locale/tr.json +69 -0
  126. package/web-dist/noVNC/app/locale/uk.json +81 -0
  127. package/web-dist/noVNC/app/locale/zh_CN.json +93 -0
  128. package/web-dist/noVNC/app/locale/zh_TW.json +69 -0
  129. package/web-dist/noVNC/app/localization.js +206 -0
  130. package/web-dist/noVNC/app/sounds/CREDITS +4 -0
  131. package/web-dist/noVNC/app/sounds/bell.mp3 +0 -0
  132. package/web-dist/noVNC/app/sounds/bell.oga +0 -0
  133. package/web-dist/noVNC/app/styles/Orbitron700.ttf +0 -0
  134. package/web-dist/noVNC/app/styles/Orbitron700.woff +0 -0
  135. package/web-dist/noVNC/app/styles/base.css +927 -0
  136. package/web-dist/noVNC/app/styles/constants.css +30 -0
  137. package/web-dist/noVNC/app/styles/input.css +628 -0
  138. package/web-dist/noVNC/app/ui.js +1873 -0
  139. package/web-dist/noVNC/app/wakelock.js +199 -0
  140. package/web-dist/noVNC/app/webutil.js +250 -0
  141. package/web-dist/noVNC/core/base64.js +104 -0
  142. package/web-dist/noVNC/core/clipboard.js +72 -0
  143. package/web-dist/noVNC/core/crypto/aes.js +178 -0
  144. package/web-dist/noVNC/core/crypto/bigint.js +34 -0
  145. package/web-dist/noVNC/core/crypto/crypto.js +90 -0
  146. package/web-dist/noVNC/core/crypto/des.js +330 -0
  147. package/web-dist/noVNC/core/crypto/dh.js +55 -0
  148. package/web-dist/noVNC/core/crypto/md5.js +82 -0
  149. package/web-dist/noVNC/core/crypto/rsa.js +132 -0
  150. package/web-dist/noVNC/core/decoders/copyrect.js +27 -0
  151. package/web-dist/noVNC/core/decoders/h264.js +321 -0
  152. package/web-dist/noVNC/core/decoders/hextile.js +181 -0
  153. package/web-dist/noVNC/core/decoders/jpeg.js +161 -0
  154. package/web-dist/noVNC/core/decoders/raw.js +59 -0
  155. package/web-dist/noVNC/core/decoders/rre.js +44 -0
  156. package/web-dist/noVNC/core/decoders/tight.js +393 -0
  157. package/web-dist/noVNC/core/decoders/tightpng.js +27 -0
  158. package/web-dist/noVNC/core/decoders/zlib.js +51 -0
  159. package/web-dist/noVNC/core/decoders/zrle.js +185 -0
  160. package/web-dist/noVNC/core/deflator.js +84 -0
  161. package/web-dist/noVNC/core/display.js +578 -0
  162. package/web-dist/noVNC/core/encodings.js +54 -0
  163. package/web-dist/noVNC/core/inflator.js +65 -0
  164. package/web-dist/noVNC/core/input/domkeytable.js +311 -0
  165. package/web-dist/noVNC/core/input/fixedkeys.js +129 -0
  166. package/web-dist/noVNC/core/input/gesturehandler.js +567 -0
  167. package/web-dist/noVNC/core/input/keyboard.js +294 -0
  168. package/web-dist/noVNC/core/input/keysym.js +616 -0
  169. package/web-dist/noVNC/core/input/keysymdef.js +688 -0
  170. package/web-dist/noVNC/core/input/util.js +191 -0
  171. package/web-dist/noVNC/core/input/vkeys.js +116 -0
  172. package/web-dist/noVNC/core/input/xtscancodes.js +173 -0
  173. package/web-dist/noVNC/core/ra2.js +312 -0
  174. package/web-dist/noVNC/core/rfb.js +3426 -0
  175. package/web-dist/noVNC/core/util/browser.js +266 -0
  176. package/web-dist/noVNC/core/util/cursor.js +249 -0
  177. package/web-dist/noVNC/core/util/element.js +32 -0
  178. package/web-dist/noVNC/core/util/events.js +138 -0
  179. package/web-dist/noVNC/core/util/eventtarget.js +35 -0
  180. package/web-dist/noVNC/core/util/int.js +15 -0
  181. package/web-dist/noVNC/core/util/logging.js +56 -0
  182. package/web-dist/noVNC/core/util/strings.js +28 -0
  183. package/web-dist/noVNC/core/websock.js +369 -0
  184. package/web-dist/noVNC/defaults.json +1 -0
  185. package/web-dist/noVNC/mandatory.json +1 -0
  186. package/web-dist/noVNC/vendor/pako/LICENSE +21 -0
  187. package/web-dist/noVNC/vendor/pako/README.md +6 -0
  188. package/web-dist/noVNC/vendor/pako/lib/utils/common.js +45 -0
  189. package/web-dist/noVNC/vendor/pako/lib/zlib/adler32.js +27 -0
  190. package/web-dist/noVNC/vendor/pako/lib/zlib/constants.js +47 -0
  191. package/web-dist/noVNC/vendor/pako/lib/zlib/crc32.js +36 -0
  192. package/web-dist/noVNC/vendor/pako/lib/zlib/deflate.js +1846 -0
  193. package/web-dist/noVNC/vendor/pako/lib/zlib/gzheader.js +35 -0
  194. package/web-dist/noVNC/vendor/pako/lib/zlib/inffast.js +324 -0
  195. package/web-dist/noVNC/vendor/pako/lib/zlib/inflate.js +1527 -0
  196. package/web-dist/noVNC/vendor/pako/lib/zlib/inftrees.js +322 -0
  197. package/web-dist/noVNC/vendor/pako/lib/zlib/messages.js +11 -0
  198. package/web-dist/noVNC/vendor/pako/lib/zlib/trees.js +1195 -0
  199. package/web-dist/noVNC/vendor/pako/lib/zlib/zstream.js +24 -0
  200. package/web-dist/noVNC/vnc_lite.html +180 -0
  201. package/web-dist/assets/channel-DlBBaM6I.js +0 -1
  202. package/web-dist/assets/classDiagram-VBA2DB6C-DixDm16f.js +0 -1
  203. package/web-dist/assets/classDiagram-v2-RAHNMMFH-DixDm16f.js +0 -1
  204. package/web-dist/assets/clone-DEDCKaxV.js +0 -1
  205. package/web-dist/assets/stateDiagram-v2-FVOUBMTO-Bdpt0e9f.js +0 -1
@@ -0,0 +1,1873 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2019 The noVNC authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ */
8
+
9
+ import * as Log from '../core/util/logging.js';
10
+ import _, { l10n } from './localization.js';
11
+ import { isTouchDevice, isMac, isIOS, isAndroid, isChromeOS, isSafari,
12
+ hasScrollbarGutter, dragThreshold, browserAsyncClipboardSupport }
13
+ from '../core/util/browser.js';
14
+ import { setCapture, getPointerEvent } from '../core/util/events.js';
15
+ import KeyTable from "../core/input/keysym.js";
16
+ import keysyms from "../core/input/keysymdef.js";
17
+ import Keyboard from "../core/input/keyboard.js";
18
+ import RFB from "../core/rfb.js";
19
+ import WakeLockManager from './wakelock.js';
20
+ import * as WebUtil from "./webutil.js";
21
+
22
+ const PAGE_TITLE = "noVNC";
23
+
24
+ const LINGUAS = ["cs", "de", "el", "es", "fr", "hr", "hu", "it", "ja", "ko", "nl", "pl", "pt_BR", "ru", "sv", "tr", "uk", "zh_CN", "zh_TW"];
25
+
26
+ const UI = {
27
+
28
+ customSettings: {},
29
+
30
+ connected: false,
31
+ desktopName: "",
32
+
33
+ statusTimeout: null,
34
+ hideKeyboardTimeout: null,
35
+ idleControlbarTimeout: null,
36
+ closeControlbarTimeout: null,
37
+
38
+ controlbarGrabbed: false,
39
+ controlbarDrag: false,
40
+ controlbarMouseDownClientY: 0,
41
+ controlbarMouseDownOffsetY: 0,
42
+
43
+ lastKeyboardinput: null,
44
+ defaultKeyboardinputLen: 100,
45
+
46
+ inhibitReconnect: true,
47
+ reconnectCallback: null,
48
+ reconnectPassword: null,
49
+
50
+ wakeLockManager: new WakeLockManager(),
51
+
52
+ async start(options={}) {
53
+ UI.customSettings = options.settings || {};
54
+ if (UI.customSettings.defaults === undefined) {
55
+ UI.customSettings.defaults = {};
56
+ }
57
+ if (UI.customSettings.mandatory === undefined) {
58
+ UI.customSettings.mandatory = {};
59
+ }
60
+
61
+ // Set up translations
62
+ try {
63
+ await l10n.setup(LINGUAS, "app/locale/");
64
+ } catch (err) {
65
+ Log.Error("Failed to load translations: " + err);
66
+ }
67
+
68
+ // Initialize setting storage
69
+ await WebUtil.initSettings();
70
+
71
+ // Wait for the page to load
72
+ if (document.readyState !== "interactive" && document.readyState !== "complete") {
73
+ await new Promise((resolve, reject) => {
74
+ document.addEventListener('DOMContentLoaded', resolve);
75
+ });
76
+ }
77
+
78
+ UI.initSettings();
79
+
80
+ // Translate the DOM
81
+ l10n.translateDOM();
82
+
83
+ // We rely on modern APIs which might not be available in an
84
+ // insecure context
85
+ if (!window.isSecureContext) {
86
+ // FIXME: This gets hidden when connecting
87
+ UI.showStatus(_("Running without HTTPS is not recommended, crashes or other issues are likely."), 'error');
88
+ }
89
+
90
+ // Try to fetch version number
91
+ try {
92
+ let response = await fetch('./package.json');
93
+ if (!response.ok) {
94
+ throw Error("" + response.status + " " + response.statusText);
95
+ }
96
+
97
+ let packageInfo = await response.json();
98
+ Array.from(document.getElementsByClassName('noVNC_version')).forEach(el => el.innerText = packageInfo.version);
99
+ } catch (err) {
100
+ Log.Error("Couldn't fetch package.json: " + err);
101
+ Array.from(document.getElementsByClassName('noVNC_version_wrapper'))
102
+ .concat(Array.from(document.getElementsByClassName('noVNC_version_separator')))
103
+ .forEach(el => el.style.display = 'none');
104
+ }
105
+
106
+ // Adapt the interface for touch screen devices
107
+ if (isTouchDevice) {
108
+ // Remove the address bar
109
+ setTimeout(() => window.scrollTo(0, 1), 100);
110
+ }
111
+
112
+ // Restore control bar position
113
+ if (WebUtil.readSetting('controlbar_pos') === 'right') {
114
+ UI.toggleControlbarSide();
115
+ }
116
+
117
+ UI.initFullscreen();
118
+
119
+ // Setup event handlers
120
+ UI.addControlbarHandlers();
121
+ UI.addTouchSpecificHandlers();
122
+ UI.addExtraKeysHandlers();
123
+ UI.addMachineHandlers();
124
+ UI.addConnectionControlHandlers();
125
+ UI.addClipboardHandlers();
126
+ UI.addSettingsHandlers();
127
+ document.getElementById("noVNC_status")
128
+ .addEventListener('click', UI.hideStatus);
129
+
130
+ // Bootstrap fallback input handler
131
+ UI.keyboardinputReset();
132
+
133
+ UI.openControlbar();
134
+
135
+ UI.updateVisualState('init');
136
+
137
+ document.documentElement.classList.remove("noVNC_loading");
138
+
139
+ let autoconnect = UI.getSetting('autoconnect');
140
+ if (autoconnect === 'true' || autoconnect == '1') {
141
+ UI.connect();
142
+ } else {
143
+ // Show the connect panel on first load unless autoconnecting
144
+ UI.openConnectPanel();
145
+ }
146
+ },
147
+
148
+ initFullscreen() {
149
+ // Only show the button if fullscreen is properly supported
150
+ // * Safari doesn't support alphanumerical input while in fullscreen
151
+ if (!isSafari() &&
152
+ (document.documentElement.requestFullscreen ||
153
+ document.documentElement.mozRequestFullScreen ||
154
+ document.documentElement.webkitRequestFullscreen ||
155
+ document.body.msRequestFullscreen)) {
156
+ document.getElementById('noVNC_fullscreen_button')
157
+ .classList.remove("noVNC_hidden");
158
+ UI.addFullscreenHandlers();
159
+ }
160
+ },
161
+
162
+ initSettings() {
163
+ // Logging selection dropdown
164
+ const llevels = ['error', 'warn', 'info', 'debug'];
165
+ for (let i = 0; i < llevels.length; i += 1) {
166
+ UI.addOption(document.getElementById('noVNC_setting_logging'), llevels[i], llevels[i]);
167
+ }
168
+
169
+ // Settings with immediate effects
170
+ UI.initSetting('logging', 'warn');
171
+ UI.updateLogging();
172
+
173
+ UI.setupSettingLabels();
174
+
175
+ /* Populate the controls if defaults are provided in the URL */
176
+ UI.initSetting('host', '');
177
+ UI.initSetting('port', 0);
178
+ UI.initSetting('encrypt', (window.location.protocol === "https:"));
179
+ UI.initSetting('password');
180
+ UI.initSetting('autoconnect', false);
181
+ UI.initSetting('view_clip', false);
182
+ UI.initSetting('resize', 'off');
183
+ UI.initSetting('quality', 6);
184
+ UI.initSetting('compression', 2);
185
+ UI.initSetting('shared', true);
186
+ UI.initSetting('bell', 'on');
187
+ UI.initSetting('view_only', false);
188
+ UI.initSetting('show_dot', false);
189
+ UI.initSetting('path', 'websockify');
190
+ UI.initSetting('repeaterID', '');
191
+ UI.initSetting('reconnect', false);
192
+ UI.initSetting('reconnect_delay', 5000);
193
+ UI.initSetting('keep_device_awake', false);
194
+ },
195
+ // Adds a link to the label elements on the corresponding input elements
196
+ setupSettingLabels() {
197
+ const labels = document.getElementsByTagName('LABEL');
198
+ for (let i = 0; i < labels.length; i++) {
199
+ const htmlFor = labels[i].htmlFor;
200
+ if (htmlFor != '') {
201
+ const elem = document.getElementById(htmlFor);
202
+ if (elem) elem.label = labels[i];
203
+ } else {
204
+ // If 'for' isn't set, use the first input element child
205
+ const children = labels[i].children;
206
+ for (let j = 0; j < children.length; j++) {
207
+ if (children[j].form !== undefined) {
208
+ children[j].label = labels[i];
209
+ break;
210
+ }
211
+ }
212
+ }
213
+ }
214
+ },
215
+
216
+ /* ------^-------
217
+ * /INIT
218
+ * ==============
219
+ * EVENT HANDLERS
220
+ * ------v------*/
221
+
222
+ addControlbarHandlers() {
223
+ document.getElementById("noVNC_control_bar")
224
+ .addEventListener('mousemove', UI.activateControlbar);
225
+ document.getElementById("noVNC_control_bar")
226
+ .addEventListener('mouseup', UI.activateControlbar);
227
+ document.getElementById("noVNC_control_bar")
228
+ .addEventListener('mousedown', UI.activateControlbar);
229
+ document.getElementById("noVNC_control_bar")
230
+ .addEventListener('keydown', UI.activateControlbar);
231
+
232
+ document.getElementById("noVNC_control_bar")
233
+ .addEventListener('mousedown', UI.keepControlbar);
234
+ document.getElementById("noVNC_control_bar")
235
+ .addEventListener('keydown', UI.keepControlbar);
236
+
237
+ document.getElementById("noVNC_view_drag_button")
238
+ .addEventListener('click', UI.toggleViewDrag);
239
+
240
+ document.getElementById("noVNC_control_bar_handle")
241
+ .addEventListener('mousedown', UI.controlbarHandleMouseDown);
242
+ document.getElementById("noVNC_control_bar_handle")
243
+ .addEventListener('mouseup', UI.controlbarHandleMouseUp);
244
+ document.getElementById("noVNC_control_bar_handle")
245
+ .addEventListener('mousemove', UI.dragControlbarHandle);
246
+ // resize events aren't available for elements
247
+ window.addEventListener('resize', UI.updateControlbarHandle);
248
+
249
+ const exps = document.getElementsByClassName("noVNC_expander");
250
+ for (let i = 0;i < exps.length;i++) {
251
+ exps[i].addEventListener('click', UI.toggleExpander);
252
+ }
253
+ },
254
+
255
+ addTouchSpecificHandlers() {
256
+ document.getElementById("noVNC_keyboard_button")
257
+ .addEventListener('click', UI.toggleVirtualKeyboard);
258
+
259
+ UI.touchKeyboard = new Keyboard(document.getElementById('noVNC_keyboardinput'));
260
+ UI.touchKeyboard.onkeyevent = UI.keyEvent;
261
+ UI.touchKeyboard.grab();
262
+ document.getElementById("noVNC_keyboardinput")
263
+ .addEventListener('input', UI.keyInput);
264
+ document.getElementById("noVNC_keyboardinput")
265
+ .addEventListener('focus', UI.onfocusVirtualKeyboard);
266
+ document.getElementById("noVNC_keyboardinput")
267
+ .addEventListener('blur', UI.onblurVirtualKeyboard);
268
+ document.getElementById("noVNC_keyboardinput")
269
+ .addEventListener('submit', () => false);
270
+
271
+ document.documentElement
272
+ .addEventListener('mousedown', UI.keepVirtualKeyboard, true);
273
+
274
+ document.getElementById("noVNC_control_bar")
275
+ .addEventListener('touchstart', UI.activateControlbar);
276
+ document.getElementById("noVNC_control_bar")
277
+ .addEventListener('touchmove', UI.activateControlbar);
278
+ document.getElementById("noVNC_control_bar")
279
+ .addEventListener('touchend', UI.activateControlbar);
280
+ document.getElementById("noVNC_control_bar")
281
+ .addEventListener('input', UI.activateControlbar);
282
+
283
+ document.getElementById("noVNC_control_bar")
284
+ .addEventListener('touchstart', UI.keepControlbar);
285
+ document.getElementById("noVNC_control_bar")
286
+ .addEventListener('input', UI.keepControlbar);
287
+
288
+ document.getElementById("noVNC_control_bar_handle")
289
+ .addEventListener('touchstart', UI.controlbarHandleMouseDown);
290
+ document.getElementById("noVNC_control_bar_handle")
291
+ .addEventListener('touchend', UI.controlbarHandleMouseUp);
292
+ document.getElementById("noVNC_control_bar_handle")
293
+ .addEventListener('touchmove', UI.dragControlbarHandle);
294
+ },
295
+
296
+ addExtraKeysHandlers() {
297
+ document.getElementById("noVNC_toggle_extra_keys_button")
298
+ .addEventListener('click', UI.toggleExtraKeys);
299
+ document.getElementById("noVNC_toggle_ctrl_button")
300
+ .addEventListener('click', UI.toggleCtrl);
301
+ document.getElementById("noVNC_toggle_windows_button")
302
+ .addEventListener('click', UI.toggleWindows);
303
+ document.getElementById("noVNC_toggle_alt_button")
304
+ .addEventListener('click', UI.toggleAlt);
305
+ document.getElementById("noVNC_send_tab_button")
306
+ .addEventListener('click', UI.sendTab);
307
+ document.getElementById("noVNC_send_esc_button")
308
+ .addEventListener('click', UI.sendEsc);
309
+ document.getElementById("noVNC_send_ctrl_alt_del_button")
310
+ .addEventListener('click', UI.sendCtrlAltDel);
311
+ },
312
+
313
+ addMachineHandlers() {
314
+ document.getElementById("noVNC_shutdown_button")
315
+ .addEventListener('click', () => UI.rfb.machineShutdown());
316
+ document.getElementById("noVNC_reboot_button")
317
+ .addEventListener('click', () => UI.rfb.machineReboot());
318
+ document.getElementById("noVNC_reset_button")
319
+ .addEventListener('click', () => UI.rfb.machineReset());
320
+ document.getElementById("noVNC_power_button")
321
+ .addEventListener('click', UI.togglePowerPanel);
322
+ },
323
+
324
+ addConnectionControlHandlers() {
325
+ document.getElementById("noVNC_disconnect_button")
326
+ .addEventListener('click', UI.disconnect);
327
+ document.getElementById("noVNC_connect_button")
328
+ .addEventListener('click', UI.connect);
329
+ document.getElementById("noVNC_cancel_reconnect_button")
330
+ .addEventListener('click', UI.cancelReconnect);
331
+
332
+ document.getElementById("noVNC_approve_server_button")
333
+ .addEventListener('click', UI.approveServer);
334
+ document.getElementById("noVNC_reject_server_button")
335
+ .addEventListener('click', UI.rejectServer);
336
+ document.getElementById("noVNC_credentials_button")
337
+ .addEventListener('click', UI.setCredentials);
338
+ },
339
+
340
+ addClipboardHandlers() {
341
+ document.getElementById("noVNC_clipboard_button")
342
+ .addEventListener('click', UI.toggleClipboardPanel);
343
+ document.getElementById("noVNC_clipboard_text")
344
+ .addEventListener('change', UI.clipboardSend);
345
+ },
346
+
347
+ // Add a call to save settings when the element changes,
348
+ // unless the optional parameter changeFunc is used instead.
349
+ addSettingChangeHandler(name, changeFunc) {
350
+ const settingElem = document.getElementById("noVNC_setting_" + name);
351
+ if (changeFunc === undefined) {
352
+ changeFunc = () => UI.saveSetting(name);
353
+ }
354
+ settingElem.addEventListener('change', changeFunc);
355
+ },
356
+
357
+ addSettingsHandlers() {
358
+ document.getElementById("noVNC_settings_button")
359
+ .addEventListener('click', UI.toggleSettingsPanel);
360
+
361
+ UI.addSettingChangeHandler('encrypt');
362
+ UI.addSettingChangeHandler('resize');
363
+ UI.addSettingChangeHandler('resize', UI.applyResizeMode);
364
+ UI.addSettingChangeHandler('resize', UI.updateViewClip);
365
+ UI.addSettingChangeHandler('quality');
366
+ UI.addSettingChangeHandler('quality', UI.updateQuality);
367
+ UI.addSettingChangeHandler('compression');
368
+ UI.addSettingChangeHandler('compression', UI.updateCompression);
369
+ UI.addSettingChangeHandler('view_clip');
370
+ UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
371
+ UI.addSettingChangeHandler('shared');
372
+ UI.addSettingChangeHandler('view_only');
373
+ UI.addSettingChangeHandler('view_only', UI.updateViewOnly);
374
+ UI.addSettingChangeHandler('show_dot');
375
+ UI.addSettingChangeHandler('show_dot', UI.updateShowDotCursor);
376
+ UI.addSettingChangeHandler('keep_device_awake');
377
+ UI.addSettingChangeHandler('keep_device_awake', UI.updateRequestWakelock);
378
+ UI.addSettingChangeHandler('host');
379
+ UI.addSettingChangeHandler('port');
380
+ UI.addSettingChangeHandler('path');
381
+ UI.addSettingChangeHandler('repeaterID');
382
+ UI.addSettingChangeHandler('logging');
383
+ UI.addSettingChangeHandler('logging', UI.updateLogging);
384
+ UI.addSettingChangeHandler('reconnect');
385
+ UI.addSettingChangeHandler('reconnect_delay');
386
+ },
387
+
388
+ addFullscreenHandlers() {
389
+ document.getElementById("noVNC_fullscreen_button")
390
+ .addEventListener('click', UI.toggleFullscreen);
391
+
392
+ window.addEventListener('fullscreenchange', UI.updateFullscreenButton);
393
+ window.addEventListener('mozfullscreenchange', UI.updateFullscreenButton);
394
+ window.addEventListener('webkitfullscreenchange', UI.updateFullscreenButton);
395
+ window.addEventListener('msfullscreenchange', UI.updateFullscreenButton);
396
+ },
397
+
398
+ /* ------^-------
399
+ * /EVENT HANDLERS
400
+ * ==============
401
+ * VISUAL
402
+ * ------v------*/
403
+
404
+ // Disable/enable controls depending on connection state
405
+ updateVisualState(state) {
406
+
407
+ document.documentElement.classList.remove("noVNC_connecting");
408
+ document.documentElement.classList.remove("noVNC_connected");
409
+ document.documentElement.classList.remove("noVNC_disconnecting");
410
+ document.documentElement.classList.remove("noVNC_reconnecting");
411
+
412
+ const transitionElem = document.getElementById("noVNC_transition_text");
413
+ switch (state) {
414
+ case 'init':
415
+ break;
416
+ case 'connecting':
417
+ transitionElem.textContent = _("Connecting...");
418
+ document.documentElement.classList.add("noVNC_connecting");
419
+ break;
420
+ case 'connected':
421
+ document.documentElement.classList.add("noVNC_connected");
422
+ break;
423
+ case 'disconnecting':
424
+ transitionElem.textContent = _("Disconnecting...");
425
+ document.documentElement.classList.add("noVNC_disconnecting");
426
+ break;
427
+ case 'disconnected':
428
+ break;
429
+ case 'reconnecting':
430
+ transitionElem.textContent = _("Reconnecting...");
431
+ document.documentElement.classList.add("noVNC_reconnecting");
432
+ break;
433
+ default:
434
+ Log.Error("Invalid visual state: " + state);
435
+ UI.showStatus(_("Internal error"), 'error');
436
+ return;
437
+ }
438
+
439
+ if (UI.connected) {
440
+ UI.updateViewClip();
441
+
442
+ UI.disableSetting('encrypt');
443
+ UI.disableSetting('shared');
444
+ UI.disableSetting('host');
445
+ UI.disableSetting('port');
446
+ UI.disableSetting('path');
447
+ UI.disableSetting('repeaterID');
448
+
449
+ // Hide the controlbar after 2 seconds
450
+ UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
451
+ } else {
452
+ UI.enableSetting('encrypt');
453
+ UI.enableSetting('shared');
454
+ UI.enableSetting('host');
455
+ UI.enableSetting('port');
456
+ UI.enableSetting('path');
457
+ UI.enableSetting('repeaterID');
458
+ UI.updatePowerButton();
459
+ UI.keepControlbar();
460
+ }
461
+
462
+ // State change closes dialogs as they may not be relevant
463
+ // anymore
464
+ UI.closeAllPanels();
465
+ document.getElementById('noVNC_verify_server_dlg')
466
+ .classList.remove('noVNC_open');
467
+ document.getElementById('noVNC_credentials_dlg')
468
+ .classList.remove('noVNC_open');
469
+ },
470
+
471
+ showStatus(text, statusType, time) {
472
+ const statusElem = document.getElementById('noVNC_status');
473
+
474
+ if (typeof statusType === 'undefined') {
475
+ statusType = 'normal';
476
+ }
477
+
478
+ // Don't overwrite more severe visible statuses and never
479
+ // errors. Only shows the first error.
480
+ if (statusElem.classList.contains("noVNC_open")) {
481
+ if (statusElem.classList.contains("noVNC_status_error")) {
482
+ return;
483
+ }
484
+ if (statusElem.classList.contains("noVNC_status_warn") &&
485
+ statusType === 'normal') {
486
+ return;
487
+ }
488
+ }
489
+
490
+ clearTimeout(UI.statusTimeout);
491
+
492
+ switch (statusType) {
493
+ case 'error':
494
+ statusElem.classList.remove("noVNC_status_warn");
495
+ statusElem.classList.remove("noVNC_status_normal");
496
+ statusElem.classList.add("noVNC_status_error");
497
+ break;
498
+ case 'warning':
499
+ case 'warn':
500
+ statusElem.classList.remove("noVNC_status_error");
501
+ statusElem.classList.remove("noVNC_status_normal");
502
+ statusElem.classList.add("noVNC_status_warn");
503
+ break;
504
+ case 'normal':
505
+ case 'info':
506
+ default:
507
+ statusElem.classList.remove("noVNC_status_error");
508
+ statusElem.classList.remove("noVNC_status_warn");
509
+ statusElem.classList.add("noVNC_status_normal");
510
+ break;
511
+ }
512
+
513
+ statusElem.textContent = text;
514
+ statusElem.classList.add("noVNC_open");
515
+
516
+ // If no time was specified, show the status for 1.5 seconds
517
+ if (typeof time === 'undefined') {
518
+ time = 1500;
519
+ }
520
+
521
+ // Error messages do not timeout
522
+ if (statusType !== 'error') {
523
+ UI.statusTimeout = window.setTimeout(UI.hideStatus, time);
524
+ }
525
+ },
526
+
527
+ hideStatus() {
528
+ clearTimeout(UI.statusTimeout);
529
+ document.getElementById('noVNC_status').classList.remove("noVNC_open");
530
+ },
531
+
532
+ activateControlbar(event) {
533
+ clearTimeout(UI.idleControlbarTimeout);
534
+ // We manipulate the anchor instead of the actual control
535
+ // bar in order to avoid creating new a stacking group
536
+ document.getElementById('noVNC_control_bar_anchor')
537
+ .classList.remove("noVNC_idle");
538
+ UI.idleControlbarTimeout = window.setTimeout(UI.idleControlbar, 2000);
539
+ },
540
+
541
+ idleControlbar() {
542
+ // Don't fade if a child of the control bar has focus
543
+ if (document.getElementById('noVNC_control_bar')
544
+ .contains(document.activeElement) && document.hasFocus()) {
545
+ UI.activateControlbar();
546
+ return;
547
+ }
548
+
549
+ document.getElementById('noVNC_control_bar_anchor')
550
+ .classList.add("noVNC_idle");
551
+ },
552
+
553
+ keepControlbar() {
554
+ clearTimeout(UI.closeControlbarTimeout);
555
+ },
556
+
557
+ openControlbar() {
558
+ document.getElementById('noVNC_control_bar')
559
+ .classList.add("noVNC_open");
560
+ },
561
+
562
+ closeControlbar() {
563
+ UI.closeAllPanels();
564
+ document.getElementById('noVNC_control_bar')
565
+ .classList.remove("noVNC_open");
566
+ UI.rfb.focus();
567
+ },
568
+
569
+ toggleControlbar() {
570
+ if (document.getElementById('noVNC_control_bar')
571
+ .classList.contains("noVNC_open")) {
572
+ UI.closeControlbar();
573
+ } else {
574
+ UI.openControlbar();
575
+ }
576
+ },
577
+
578
+ toggleControlbarSide() {
579
+ // Temporarily disable animation, if bar is displayed, to avoid weird
580
+ // movement. The transitionend-event will not fire when display=none.
581
+ const bar = document.getElementById('noVNC_control_bar');
582
+ const barDisplayStyle = window.getComputedStyle(bar).display;
583
+ if (barDisplayStyle !== 'none') {
584
+ bar.style.transitionDuration = '0s';
585
+ bar.addEventListener('transitionend', () => bar.style.transitionDuration = '');
586
+ }
587
+
588
+ const anchor = document.getElementById('noVNC_control_bar_anchor');
589
+ if (anchor.classList.contains("noVNC_right")) {
590
+ WebUtil.writeSetting('controlbar_pos', 'left');
591
+ anchor.classList.remove("noVNC_right");
592
+ } else {
593
+ WebUtil.writeSetting('controlbar_pos', 'right');
594
+ anchor.classList.add("noVNC_right");
595
+ }
596
+
597
+ // Consider this a movement of the handle
598
+ UI.controlbarDrag = true;
599
+
600
+ // The user has "followed" hint, let's hide it until the next drag
601
+ UI.showControlbarHint(false, false);
602
+ },
603
+
604
+ showControlbarHint(show, animate=true) {
605
+ const hint = document.getElementById('noVNC_control_bar_hint');
606
+
607
+ if (animate) {
608
+ hint.classList.remove("noVNC_notransition");
609
+ } else {
610
+ hint.classList.add("noVNC_notransition");
611
+ }
612
+
613
+ if (show) {
614
+ hint.classList.add("noVNC_active");
615
+ } else {
616
+ hint.classList.remove("noVNC_active");
617
+ }
618
+ },
619
+
620
+ dragControlbarHandle(e) {
621
+ if (!UI.controlbarGrabbed) return;
622
+
623
+ const ptr = getPointerEvent(e);
624
+
625
+ const anchor = document.getElementById('noVNC_control_bar_anchor');
626
+ if (ptr.clientX < (window.innerWidth * 0.1)) {
627
+ if (anchor.classList.contains("noVNC_right")) {
628
+ UI.toggleControlbarSide();
629
+ }
630
+ } else if (ptr.clientX > (window.innerWidth * 0.9)) {
631
+ if (!anchor.classList.contains("noVNC_right")) {
632
+ UI.toggleControlbarSide();
633
+ }
634
+ }
635
+
636
+ if (!UI.controlbarDrag) {
637
+ const dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY);
638
+
639
+ if (dragDistance < dragThreshold) return;
640
+
641
+ UI.controlbarDrag = true;
642
+ }
643
+
644
+ const eventY = ptr.clientY - UI.controlbarMouseDownOffsetY;
645
+
646
+ UI.moveControlbarHandle(eventY);
647
+
648
+ e.preventDefault();
649
+ e.stopPropagation();
650
+ UI.keepControlbar();
651
+ UI.activateControlbar();
652
+ },
653
+
654
+ // Move the handle but don't allow any position outside the bounds
655
+ moveControlbarHandle(viewportRelativeY) {
656
+ const handle = document.getElementById("noVNC_control_bar_handle");
657
+ const handleHeight = handle.getBoundingClientRect().height;
658
+ const controlbarBounds = document.getElementById("noVNC_control_bar")
659
+ .getBoundingClientRect();
660
+ const margin = 10;
661
+
662
+ // These heights need to be non-zero for the below logic to work
663
+ if (handleHeight === 0 || controlbarBounds.height === 0) {
664
+ return;
665
+ }
666
+
667
+ let newY = viewportRelativeY;
668
+
669
+ // Check if the coordinates are outside the control bar
670
+ if (newY < controlbarBounds.top + margin) {
671
+ // Force coordinates to be below the top of the control bar
672
+ newY = controlbarBounds.top + margin;
673
+
674
+ } else if (newY > controlbarBounds.top +
675
+ controlbarBounds.height - handleHeight - margin) {
676
+ // Force coordinates to be above the bottom of the control bar
677
+ newY = controlbarBounds.top +
678
+ controlbarBounds.height - handleHeight - margin;
679
+ }
680
+
681
+ // Corner case: control bar too small for stable position
682
+ if (controlbarBounds.height < (handleHeight + margin * 2)) {
683
+ newY = controlbarBounds.top +
684
+ (controlbarBounds.height - handleHeight) / 2;
685
+ }
686
+
687
+ // The transform needs coordinates that are relative to the parent
688
+ const parentRelativeY = newY - controlbarBounds.top;
689
+ handle.style.transform = "translateY(" + parentRelativeY + "px)";
690
+ },
691
+
692
+ updateControlbarHandle() {
693
+ // Since the control bar is fixed on the viewport and not the page,
694
+ // the move function expects coordinates relative the the viewport.
695
+ const handle = document.getElementById("noVNC_control_bar_handle");
696
+ const handleBounds = handle.getBoundingClientRect();
697
+ UI.moveControlbarHandle(handleBounds.top);
698
+ },
699
+
700
+ controlbarHandleMouseUp(e) {
701
+ if ((e.type == "mouseup") && (e.button != 0)) return;
702
+
703
+ // mouseup and mousedown on the same place toggles the controlbar
704
+ if (UI.controlbarGrabbed && !UI.controlbarDrag) {
705
+ UI.toggleControlbar();
706
+ e.preventDefault();
707
+ e.stopPropagation();
708
+ UI.keepControlbar();
709
+ UI.activateControlbar();
710
+ }
711
+ UI.controlbarGrabbed = false;
712
+ UI.showControlbarHint(false);
713
+ },
714
+
715
+ controlbarHandleMouseDown(e) {
716
+ if ((e.type == "mousedown") && (e.button != 0)) return;
717
+
718
+ const ptr = getPointerEvent(e);
719
+
720
+ const handle = document.getElementById("noVNC_control_bar_handle");
721
+ const bounds = handle.getBoundingClientRect();
722
+
723
+ // Touch events have implicit capture
724
+ if (e.type === "mousedown") {
725
+ setCapture(handle);
726
+ }
727
+
728
+ UI.controlbarGrabbed = true;
729
+ UI.controlbarDrag = false;
730
+
731
+ UI.showControlbarHint(true);
732
+
733
+ UI.controlbarMouseDownClientY = ptr.clientY;
734
+ UI.controlbarMouseDownOffsetY = ptr.clientY - bounds.top;
735
+ e.preventDefault();
736
+ e.stopPropagation();
737
+ UI.keepControlbar();
738
+ UI.activateControlbar();
739
+ },
740
+
741
+ toggleExpander(e) {
742
+ if (this.classList.contains("noVNC_open")) {
743
+ this.classList.remove("noVNC_open");
744
+ } else {
745
+ this.classList.add("noVNC_open");
746
+ }
747
+ },
748
+
749
+ /* ------^-------
750
+ * /VISUAL
751
+ * ==============
752
+ * SETTINGS
753
+ * ------v------*/
754
+
755
+ // Initial page load read/initialization of settings
756
+ initSetting(name, defVal) {
757
+ // Has the user overridden the default value?
758
+ if (name in UI.customSettings.defaults) {
759
+ defVal = UI.customSettings.defaults[name];
760
+ }
761
+ // Check Query string followed by cookie
762
+ let val = WebUtil.getConfigVar(name);
763
+ if (val === null) {
764
+ val = WebUtil.readSetting(name, defVal);
765
+ }
766
+ WebUtil.setSetting(name, val);
767
+ UI.updateSetting(name);
768
+ // Has the user forced a value?
769
+ if (name in UI.customSettings.mandatory) {
770
+ val = UI.customSettings.mandatory[name];
771
+ UI.forceSetting(name, val);
772
+ }
773
+ return val;
774
+ },
775
+
776
+ // Set the new value, update and disable form control setting
777
+ forceSetting(name, val) {
778
+ WebUtil.setSetting(name, val);
779
+ UI.updateSetting(name);
780
+ UI.disableSetting(name);
781
+ },
782
+
783
+ // Update cookie and form control setting. If value is not set, then
784
+ // updates from control to current cookie setting.
785
+ updateSetting(name) {
786
+
787
+ // Update the settings control
788
+ let value = UI.getSetting(name);
789
+
790
+ const ctrl = document.getElementById('noVNC_setting_' + name);
791
+ if (ctrl === null) {
792
+ return;
793
+ }
794
+
795
+ if (ctrl.type === 'checkbox') {
796
+ ctrl.checked = value;
797
+ } else if (typeof ctrl.options !== 'undefined') {
798
+ for (let i = 0; i < ctrl.options.length; i += 1) {
799
+ if (ctrl.options[i].value === value) {
800
+ ctrl.selectedIndex = i;
801
+ break;
802
+ }
803
+ }
804
+ } else {
805
+ ctrl.value = value;
806
+ }
807
+ },
808
+
809
+ // Save control setting to cookie
810
+ saveSetting(name) {
811
+ const ctrl = document.getElementById('noVNC_setting_' + name);
812
+ let val;
813
+ if (ctrl.type === 'checkbox') {
814
+ val = ctrl.checked;
815
+ } else if (typeof ctrl.options !== 'undefined') {
816
+ val = ctrl.options[ctrl.selectedIndex].value;
817
+ } else {
818
+ val = ctrl.value;
819
+ }
820
+ WebUtil.writeSetting(name, val);
821
+ //Log.Debug("Setting saved '" + name + "=" + val + "'");
822
+ return val;
823
+ },
824
+
825
+ // Read form control compatible setting from cookie
826
+ getSetting(name) {
827
+ const ctrl = document.getElementById('noVNC_setting_' + name);
828
+ let val = WebUtil.readSetting(name);
829
+ if (typeof val !== 'undefined' && val !== null &&
830
+ ctrl !== null && ctrl.type === 'checkbox') {
831
+ if (val.toString().toLowerCase() in {'0': 1, 'no': 1, 'false': 1}) {
832
+ val = false;
833
+ } else {
834
+ val = true;
835
+ }
836
+ }
837
+ return val;
838
+ },
839
+
840
+ // These helpers compensate for the lack of parent-selectors and
841
+ // previous-sibling-selectors in CSS which are needed when we want to
842
+ // disable the labels that belong to disabled input elements.
843
+ disableSetting(name) {
844
+ const ctrl = document.getElementById('noVNC_setting_' + name);
845
+ if (ctrl !== null) {
846
+ ctrl.disabled = true;
847
+ if (ctrl.label !== undefined) {
848
+ ctrl.label.classList.add('noVNC_disabled');
849
+ }
850
+ }
851
+ },
852
+
853
+ enableSetting(name) {
854
+ const ctrl = document.getElementById('noVNC_setting_' + name);
855
+ if (ctrl !== null) {
856
+ ctrl.disabled = false;
857
+ if (ctrl.label !== undefined) {
858
+ ctrl.label.classList.remove('noVNC_disabled');
859
+ }
860
+ }
861
+ },
862
+
863
+ /* ------^-------
864
+ * /SETTINGS
865
+ * ==============
866
+ * PANELS
867
+ * ------v------*/
868
+
869
+ closeAllPanels() {
870
+ UI.closeSettingsPanel();
871
+ UI.closePowerPanel();
872
+ UI.closeClipboardPanel();
873
+ UI.closeExtraKeys();
874
+ },
875
+
876
+ /* ------^-------
877
+ * /PANELS
878
+ * ==============
879
+ * SETTINGS (panel)
880
+ * ------v------*/
881
+
882
+ openSettingsPanel() {
883
+ UI.closeAllPanels();
884
+ UI.openControlbar();
885
+
886
+ // Refresh UI elements from saved cookies
887
+ UI.updateSetting('encrypt');
888
+ UI.updateSetting('view_clip');
889
+ UI.updateSetting('resize');
890
+ UI.updateSetting('quality');
891
+ UI.updateSetting('compression');
892
+ UI.updateSetting('shared');
893
+ UI.updateSetting('view_only');
894
+ UI.updateSetting('path');
895
+ UI.updateSetting('repeaterID');
896
+ UI.updateSetting('logging');
897
+ UI.updateSetting('reconnect');
898
+ UI.updateSetting('reconnect_delay');
899
+
900
+ document.getElementById('noVNC_settings')
901
+ .classList.add("noVNC_open");
902
+ document.getElementById('noVNC_settings_button')
903
+ .classList.add("noVNC_selected");
904
+ },
905
+
906
+ closeSettingsPanel() {
907
+ document.getElementById('noVNC_settings')
908
+ .classList.remove("noVNC_open");
909
+ document.getElementById('noVNC_settings_button')
910
+ .classList.remove("noVNC_selected");
911
+ },
912
+
913
+ toggleSettingsPanel() {
914
+ if (document.getElementById('noVNC_settings')
915
+ .classList.contains("noVNC_open")) {
916
+ UI.closeSettingsPanel();
917
+ } else {
918
+ UI.openSettingsPanel();
919
+ }
920
+ },
921
+
922
+ /* ------^-------
923
+ * /SETTINGS
924
+ * ==============
925
+ * POWER
926
+ * ------v------*/
927
+
928
+ openPowerPanel() {
929
+ UI.closeAllPanels();
930
+ UI.openControlbar();
931
+
932
+ document.getElementById('noVNC_power')
933
+ .classList.add("noVNC_open");
934
+ document.getElementById('noVNC_power_button')
935
+ .classList.add("noVNC_selected");
936
+ },
937
+
938
+ closePowerPanel() {
939
+ document.getElementById('noVNC_power')
940
+ .classList.remove("noVNC_open");
941
+ document.getElementById('noVNC_power_button')
942
+ .classList.remove("noVNC_selected");
943
+ },
944
+
945
+ togglePowerPanel() {
946
+ if (document.getElementById('noVNC_power')
947
+ .classList.contains("noVNC_open")) {
948
+ UI.closePowerPanel();
949
+ } else {
950
+ UI.openPowerPanel();
951
+ }
952
+ },
953
+
954
+ // Disable/enable power button
955
+ updatePowerButton() {
956
+ if (UI.connected &&
957
+ UI.rfb.capabilities.power &&
958
+ !UI.rfb.viewOnly) {
959
+ document.getElementById('noVNC_power_button')
960
+ .classList.remove("noVNC_hidden");
961
+ } else {
962
+ document.getElementById('noVNC_power_button')
963
+ .classList.add("noVNC_hidden");
964
+ // Close power panel if open
965
+ UI.closePowerPanel();
966
+ }
967
+ },
968
+
969
+ /* ------^-------
970
+ * /POWER
971
+ * ==============
972
+ * CLIPBOARD
973
+ * ------v------*/
974
+
975
+ openClipboardPanel() {
976
+ UI.closeAllPanels();
977
+ UI.openControlbar();
978
+
979
+ document.getElementById('noVNC_clipboard')
980
+ .classList.add("noVNC_open");
981
+ document.getElementById('noVNC_clipboard_button')
982
+ .classList.add("noVNC_selected");
983
+ },
984
+
985
+ closeClipboardPanel() {
986
+ document.getElementById('noVNC_clipboard')
987
+ .classList.remove("noVNC_open");
988
+ document.getElementById('noVNC_clipboard_button')
989
+ .classList.remove("noVNC_selected");
990
+ },
991
+
992
+ toggleClipboardPanel() {
993
+ if (document.getElementById('noVNC_clipboard')
994
+ .classList.contains("noVNC_open")) {
995
+ UI.closeClipboardPanel();
996
+ } else {
997
+ UI.openClipboardPanel();
998
+ }
999
+ },
1000
+
1001
+ clipboardReceive(e) {
1002
+ Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "...");
1003
+ document.getElementById('noVNC_clipboard_text').value = e.detail.text;
1004
+ Log.Debug("<< UI.clipboardReceive");
1005
+ },
1006
+
1007
+ clipboardSend() {
1008
+ const text = document.getElementById('noVNC_clipboard_text').value;
1009
+ Log.Debug(">> UI.clipboardSend: " + text.substr(0, 40) + "...");
1010
+ UI.rfb.clipboardPasteFrom(text);
1011
+ Log.Debug("<< UI.clipboardSend");
1012
+ },
1013
+
1014
+ /* ------^-------
1015
+ * /CLIPBOARD
1016
+ * ==============
1017
+ * CONNECTION
1018
+ * ------v------*/
1019
+
1020
+ openConnectPanel() {
1021
+ document.getElementById('noVNC_connect_dlg')
1022
+ .classList.add("noVNC_open");
1023
+ },
1024
+
1025
+ closeConnectPanel() {
1026
+ document.getElementById('noVNC_connect_dlg')
1027
+ .classList.remove("noVNC_open");
1028
+ },
1029
+
1030
+ connect(event, password) {
1031
+
1032
+ // Ignore when rfb already exists
1033
+ if (typeof UI.rfb !== 'undefined') {
1034
+ return;
1035
+ }
1036
+
1037
+ const host = UI.getSetting('host');
1038
+ const port = UI.getSetting('port');
1039
+ const path = UI.getSetting('path');
1040
+
1041
+ if (typeof password === 'undefined') {
1042
+ password = UI.getSetting('password');
1043
+ UI.reconnectPassword = password;
1044
+ }
1045
+
1046
+ if (password === null) {
1047
+ password = undefined;
1048
+ }
1049
+
1050
+ UI.hideStatus();
1051
+
1052
+ UI.closeConnectPanel();
1053
+
1054
+ UI.updateVisualState('connecting');
1055
+
1056
+ let url;
1057
+
1058
+ if (host) {
1059
+ url = new URL("https://" + host);
1060
+
1061
+ url.protocol = UI.getSetting('encrypt') ? 'wss:' : 'ws:';
1062
+ if (port) {
1063
+ url.port = port;
1064
+ }
1065
+
1066
+ // "./" is needed to force URL() to interpret the path-variable as
1067
+ // a path and not as an URL. This is relevant if for example path
1068
+ // starts with more than one "/", in which case it would be
1069
+ // interpreted as a host name instead.
1070
+ url = new URL("./" + path, url);
1071
+ } else {
1072
+ // Current (May 2024) browsers support relative WebSocket
1073
+ // URLs natively, but we need to support older browsers for
1074
+ // some time.
1075
+ url = new URL(path, location.href);
1076
+ url.protocol = (window.location.protocol === "https:") ? 'wss:' : 'ws:';
1077
+ }
1078
+
1079
+ if (UI.getSetting('keep_device_awake')) {
1080
+ UI.wakeLockManager.acquire();
1081
+ }
1082
+
1083
+ try {
1084
+ UI.rfb = new RFB(document.getElementById('noVNC_container'),
1085
+ url.href,
1086
+ { shared: UI.getSetting('shared'),
1087
+ repeaterID: UI.getSetting('repeaterID'),
1088
+ credentials: { password: password } });
1089
+ } catch (exc) {
1090
+ Log.Error("Failed to connect to server: " + exc);
1091
+ UI.updateVisualState('disconnected');
1092
+ UI.showStatus(_("Failed to connect to server: ") + exc, 'error');
1093
+ return;
1094
+ }
1095
+
1096
+ UI.rfb.addEventListener("connect", UI.connectFinished);
1097
+ UI.rfb.addEventListener("disconnect", UI.disconnectFinished);
1098
+ UI.rfb.addEventListener("serververification", UI.serverVerify);
1099
+ UI.rfb.addEventListener("credentialsrequired", UI.credentials);
1100
+ UI.rfb.addEventListener("securityfailure", UI.securityFailed);
1101
+ UI.rfb.addEventListener("clippingviewport", UI.updateViewDrag);
1102
+ UI.rfb.addEventListener("capabilities", UI.updatePowerButton);
1103
+ UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
1104
+ UI.rfb.addEventListener("bell", UI.bell);
1105
+ UI.rfb.addEventListener("desktopname", UI.updateDesktopName);
1106
+ UI.rfb.clipViewport = UI.getSetting('view_clip');
1107
+ UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
1108
+ UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
1109
+ UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
1110
+ UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
1111
+ UI.rfb.showDotCursor = UI.getSetting('show_dot');
1112
+
1113
+ UI.updateViewOnly(); // requires UI.rfb
1114
+ UI.updateClipboard();
1115
+ },
1116
+
1117
+ disconnect() {
1118
+ UI.rfb.disconnect();
1119
+
1120
+ UI.connected = false;
1121
+
1122
+ // Disable automatic reconnecting
1123
+ UI.inhibitReconnect = true;
1124
+
1125
+ UI.updateVisualState('disconnecting');
1126
+
1127
+ // Don't display the connection settings until we're actually disconnected
1128
+ },
1129
+
1130
+ reconnect() {
1131
+ UI.reconnectCallback = null;
1132
+
1133
+ // if reconnect has been disabled in the meantime, do nothing.
1134
+ if (UI.inhibitReconnect) {
1135
+ return;
1136
+ }
1137
+
1138
+ UI.connect(null, UI.reconnectPassword);
1139
+ },
1140
+
1141
+ cancelReconnect() {
1142
+ if (UI.reconnectCallback !== null) {
1143
+ clearTimeout(UI.reconnectCallback);
1144
+ UI.reconnectCallback = null;
1145
+ }
1146
+
1147
+ UI.updateVisualState('disconnected');
1148
+
1149
+ UI.openControlbar();
1150
+ UI.openConnectPanel();
1151
+ },
1152
+
1153
+ connectFinished(e) {
1154
+ UI.connected = true;
1155
+ UI.inhibitReconnect = false;
1156
+
1157
+ let msg;
1158
+ if (UI.getSetting('encrypt')) {
1159
+ msg = _("Connected (encrypted) to ") + UI.desktopName;
1160
+ } else {
1161
+ msg = _("Connected (unencrypted) to ") + UI.desktopName;
1162
+ }
1163
+ UI.showStatus(msg);
1164
+ UI.updateVisualState('connected');
1165
+
1166
+ UI.updateBeforeUnload();
1167
+
1168
+ // Do this last because it can only be used on rendered elements
1169
+ UI.rfb.focus();
1170
+ },
1171
+
1172
+ disconnectFinished(e) {
1173
+ const wasConnected = UI.connected;
1174
+
1175
+ // This variable is ideally set when disconnection starts, but
1176
+ // when the disconnection isn't clean or if it is initiated by
1177
+ // the server, we need to do it here as well since
1178
+ // UI.disconnect() won't be used in those cases.
1179
+ UI.connected = false;
1180
+
1181
+ UI.rfb = undefined;
1182
+ UI.wakeLockManager.release();
1183
+
1184
+ if (!e.detail.clean) {
1185
+ UI.updateVisualState('disconnected');
1186
+ if (wasConnected) {
1187
+ UI.showStatus(_("Something went wrong, connection is closed"),
1188
+ 'error');
1189
+ } else {
1190
+ UI.showStatus(_("Failed to connect to server"), 'error');
1191
+ }
1192
+ }
1193
+ // If reconnecting is allowed process it now
1194
+ if (UI.getSetting('reconnect', false) === true && !UI.inhibitReconnect) {
1195
+ UI.updateVisualState('reconnecting');
1196
+
1197
+ const delay = parseInt(UI.getSetting('reconnect_delay'));
1198
+ UI.reconnectCallback = setTimeout(UI.reconnect, delay);
1199
+ return;
1200
+ } else {
1201
+ UI.updateVisualState('disconnected');
1202
+ UI.showStatus(_("Disconnected"), 'normal');
1203
+ }
1204
+
1205
+ UI.updateBeforeUnload();
1206
+
1207
+ document.title = PAGE_TITLE;
1208
+
1209
+ UI.openControlbar();
1210
+ UI.openConnectPanel();
1211
+ },
1212
+
1213
+ securityFailed(e) {
1214
+ let msg;
1215
+ // On security failures we might get a string with a reason
1216
+ // directly from the server. Note that we can't control if
1217
+ // this string is translated or not.
1218
+ if ('reason' in e.detail) {
1219
+ msg = _("New connection has been rejected with reason: ") +
1220
+ e.detail.reason;
1221
+ } else {
1222
+ msg = _("New connection has been rejected");
1223
+ }
1224
+ UI.showStatus(msg, 'error');
1225
+ },
1226
+
1227
+ handleBeforeUnload(e) {
1228
+ // Trigger a "Leave site?" warning prompt before closing the
1229
+ // page. Modern browsers (Oct 2025) accept either (or both)
1230
+ // preventDefault() or a nonempty returnValue, though the latter is
1231
+ // considered legacy. The custom string is ignored by modern browsers,
1232
+ // which display a native message, but older browsers will show it.
1233
+ e.preventDefault();
1234
+ e.returnValue = _("Are you sure you want to disconnect the session?");
1235
+ },
1236
+
1237
+ updateBeforeUnload() {
1238
+ // Remove first to avoid adding duplicates
1239
+ window.removeEventListener("beforeunload", UI.handleBeforeUnload);
1240
+ if (!UI.rfb?.viewOnly && UI.connected) {
1241
+ window.addEventListener("beforeunload", UI.handleBeforeUnload);
1242
+ }
1243
+ },
1244
+
1245
+ /* ------^-------
1246
+ * /CONNECTION
1247
+ * ==============
1248
+ * SERVER VERIFY
1249
+ * ------v------*/
1250
+
1251
+ async serverVerify(e) {
1252
+ const type = e.detail.type;
1253
+ if (type === 'RSA') {
1254
+ const publickey = e.detail.publickey;
1255
+ let fingerprint = await window.crypto.subtle.digest("SHA-1", publickey);
1256
+ // The same fingerprint format as RealVNC
1257
+ fingerprint = Array.from(new Uint8Array(fingerprint).slice(0, 8)).map(
1258
+ x => x.toString(16).padStart(2, '0')).join('-');
1259
+ document.getElementById('noVNC_verify_server_dlg').classList.add('noVNC_open');
1260
+ document.getElementById('noVNC_fingerprint').innerHTML = fingerprint;
1261
+ }
1262
+ },
1263
+
1264
+ approveServer(e) {
1265
+ e.preventDefault();
1266
+ document.getElementById('noVNC_verify_server_dlg').classList.remove('noVNC_open');
1267
+ UI.rfb.approveServer();
1268
+ },
1269
+
1270
+ rejectServer(e) {
1271
+ e.preventDefault();
1272
+ document.getElementById('noVNC_verify_server_dlg').classList.remove('noVNC_open');
1273
+ UI.disconnect();
1274
+ },
1275
+
1276
+ /* ------^-------
1277
+ * /SERVER VERIFY
1278
+ * ==============
1279
+ * PASSWORD
1280
+ * ------v------*/
1281
+
1282
+ credentials(e) {
1283
+ // FIXME: handle more types
1284
+
1285
+ document.getElementById("noVNC_username_block").classList.remove("noVNC_hidden");
1286
+ document.getElementById("noVNC_password_block").classList.remove("noVNC_hidden");
1287
+
1288
+ let inputFocus = "none";
1289
+ if (e.detail.types.indexOf("username") === -1) {
1290
+ document.getElementById("noVNC_username_block").classList.add("noVNC_hidden");
1291
+ } else {
1292
+ inputFocus = inputFocus === "none" ? "noVNC_username_input" : inputFocus;
1293
+ }
1294
+ if (e.detail.types.indexOf("password") === -1) {
1295
+ document.getElementById("noVNC_password_block").classList.add("noVNC_hidden");
1296
+ } else {
1297
+ inputFocus = inputFocus === "none" ? "noVNC_password_input" : inputFocus;
1298
+ }
1299
+ document.getElementById('noVNC_credentials_dlg')
1300
+ .classList.add('noVNC_open');
1301
+
1302
+ setTimeout(() => document
1303
+ .getElementById(inputFocus).focus(), 100);
1304
+
1305
+ Log.Warn("Server asked for credentials");
1306
+ UI.showStatus(_("Credentials are required"), "warning");
1307
+ },
1308
+
1309
+ setCredentials(e) {
1310
+ // Prevent actually submitting the form
1311
+ e.preventDefault();
1312
+
1313
+ let inputElemUsername = document.getElementById('noVNC_username_input');
1314
+ const username = inputElemUsername.value;
1315
+
1316
+ let inputElemPassword = document.getElementById('noVNC_password_input');
1317
+ const password = inputElemPassword.value;
1318
+ // Clear the input after reading the password
1319
+ inputElemPassword.value = "";
1320
+
1321
+ UI.rfb.sendCredentials({ username: username, password: password });
1322
+ UI.reconnectPassword = password;
1323
+ document.getElementById('noVNC_credentials_dlg')
1324
+ .classList.remove('noVNC_open');
1325
+ },
1326
+
1327
+ /* ------^-------
1328
+ * /PASSWORD
1329
+ * ==============
1330
+ * FULLSCREEN
1331
+ * ------v------*/
1332
+
1333
+ toggleFullscreen() {
1334
+ if (document.fullscreenElement || // alternative standard method
1335
+ document.mozFullScreenElement || // currently working methods
1336
+ document.webkitFullscreenElement ||
1337
+ document.msFullscreenElement) {
1338
+ if (document.exitFullscreen) {
1339
+ document.exitFullscreen();
1340
+ } else if (document.mozCancelFullScreen) {
1341
+ document.mozCancelFullScreen();
1342
+ } else if (document.webkitExitFullscreen) {
1343
+ document.webkitExitFullscreen();
1344
+ } else if (document.msExitFullscreen) {
1345
+ document.msExitFullscreen();
1346
+ }
1347
+ } else {
1348
+ if (document.documentElement.requestFullscreen) {
1349
+ document.documentElement.requestFullscreen();
1350
+ } else if (document.documentElement.mozRequestFullScreen) {
1351
+ document.documentElement.mozRequestFullScreen();
1352
+ } else if (document.documentElement.webkitRequestFullscreen) {
1353
+ document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
1354
+ } else if (document.body.msRequestFullscreen) {
1355
+ document.body.msRequestFullscreen();
1356
+ }
1357
+ }
1358
+ UI.updateFullscreenButton();
1359
+ },
1360
+
1361
+ updateFullscreenButton() {
1362
+ if (document.fullscreenElement || // alternative standard method
1363
+ document.mozFullScreenElement || // currently working methods
1364
+ document.webkitFullscreenElement ||
1365
+ document.msFullscreenElement ) {
1366
+ document.getElementById('noVNC_fullscreen_button')
1367
+ .classList.add("noVNC_selected");
1368
+ } else {
1369
+ document.getElementById('noVNC_fullscreen_button')
1370
+ .classList.remove("noVNC_selected");
1371
+ }
1372
+ },
1373
+
1374
+ /* ------^-------
1375
+ * /FULLSCREEN
1376
+ * ==============
1377
+ * RESIZE
1378
+ * ------v------*/
1379
+
1380
+ // Apply remote resizing or local scaling
1381
+ applyResizeMode() {
1382
+ if (!UI.rfb) return;
1383
+
1384
+ UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
1385
+ UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
1386
+ },
1387
+
1388
+ /* ------^-------
1389
+ * /RESIZE
1390
+ * ==============
1391
+ * VIEW CLIPPING
1392
+ * ------v------*/
1393
+
1394
+ // Update viewport clipping property for the connection. The normal
1395
+ // case is to get the value from the setting. There are special cases
1396
+ // for when the viewport is scaled or when a touch device is used.
1397
+ updateViewClip() {
1398
+ if (!UI.rfb) return;
1399
+
1400
+ const scaling = UI.getSetting('resize') === 'scale';
1401
+
1402
+ // Some platforms have overlay scrollbars that are difficult
1403
+ // to use in our case, which means we have to force panning
1404
+ // FIXME: Working scrollbars can still be annoying to use with
1405
+ // touch, so we should ideally be able to have both
1406
+ // panning and scrollbars at the same time
1407
+
1408
+ let brokenScrollbars = false;
1409
+
1410
+ if (!hasScrollbarGutter) {
1411
+ if (isIOS() || isAndroid() || isMac() || isChromeOS()) {
1412
+ brokenScrollbars = true;
1413
+ }
1414
+ }
1415
+
1416
+ if (scaling) {
1417
+ // Can't be clipping if viewport is scaled to fit
1418
+ UI.forceSetting('view_clip', false);
1419
+ UI.rfb.clipViewport = false;
1420
+ } else if (brokenScrollbars) {
1421
+ UI.forceSetting('view_clip', true);
1422
+ UI.rfb.clipViewport = true;
1423
+ } else {
1424
+ UI.enableSetting('view_clip');
1425
+ UI.rfb.clipViewport = UI.getSetting('view_clip');
1426
+ }
1427
+
1428
+ // Changing the viewport may change the state of
1429
+ // the dragging button
1430
+ UI.updateViewDrag();
1431
+ },
1432
+
1433
+ /* ------^-------
1434
+ * /VIEW CLIPPING
1435
+ * ==============
1436
+ * VIEWDRAG
1437
+ * ------v------*/
1438
+
1439
+ toggleViewDrag() {
1440
+ if (!UI.rfb) return;
1441
+
1442
+ UI.rfb.dragViewport = !UI.rfb.dragViewport;
1443
+ UI.updateViewDrag();
1444
+ },
1445
+
1446
+ updateViewDrag() {
1447
+ if (!UI.connected) return;
1448
+
1449
+ const viewDragButton = document.getElementById('noVNC_view_drag_button');
1450
+
1451
+ if ((!UI.rfb.clipViewport || !UI.rfb.clippingViewport) &&
1452
+ UI.rfb.dragViewport) {
1453
+ // We are no longer clipping the viewport. Make sure
1454
+ // viewport drag isn't active when it can't be used.
1455
+ UI.rfb.dragViewport = false;
1456
+ }
1457
+
1458
+ if (UI.rfb.dragViewport) {
1459
+ viewDragButton.classList.add("noVNC_selected");
1460
+ } else {
1461
+ viewDragButton.classList.remove("noVNC_selected");
1462
+ }
1463
+
1464
+ if (UI.rfb.clipViewport) {
1465
+ viewDragButton.classList.remove("noVNC_hidden");
1466
+ } else {
1467
+ viewDragButton.classList.add("noVNC_hidden");
1468
+ }
1469
+
1470
+ viewDragButton.disabled = !UI.rfb.clippingViewport;
1471
+ },
1472
+
1473
+ /* ------^-------
1474
+ * /VIEWDRAG
1475
+ * ==============
1476
+ * QUALITY
1477
+ * ------v------*/
1478
+
1479
+ updateQuality() {
1480
+ if (!UI.rfb) return;
1481
+
1482
+ UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
1483
+ },
1484
+
1485
+ /* ------^-------
1486
+ * /QUALITY
1487
+ * ==============
1488
+ * COMPRESSION
1489
+ * ------v------*/
1490
+
1491
+ updateCompression() {
1492
+ if (!UI.rfb) return;
1493
+
1494
+ UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
1495
+ },
1496
+
1497
+ /* ------^-------
1498
+ * /COMPRESSION
1499
+ * ==============
1500
+ * KEYBOARD
1501
+ * ------v------*/
1502
+
1503
+ showVirtualKeyboard() {
1504
+ if (!isTouchDevice) return;
1505
+
1506
+ const input = document.getElementById('noVNC_keyboardinput');
1507
+
1508
+ if (document.activeElement == input) return;
1509
+
1510
+ input.focus();
1511
+
1512
+ try {
1513
+ const l = input.value.length;
1514
+ // Move the caret to the end
1515
+ input.setSelectionRange(l, l);
1516
+ } catch (err) {
1517
+ // setSelectionRange is undefined in Google Chrome
1518
+ }
1519
+ },
1520
+
1521
+ hideVirtualKeyboard() {
1522
+ if (!isTouchDevice) return;
1523
+
1524
+ const input = document.getElementById('noVNC_keyboardinput');
1525
+
1526
+ if (document.activeElement != input) return;
1527
+
1528
+ input.blur();
1529
+ },
1530
+
1531
+ toggleVirtualKeyboard() {
1532
+ if (document.getElementById('noVNC_keyboard_button')
1533
+ .classList.contains("noVNC_selected")) {
1534
+ UI.hideVirtualKeyboard();
1535
+ } else {
1536
+ UI.showVirtualKeyboard();
1537
+ }
1538
+ },
1539
+
1540
+ onfocusVirtualKeyboard(event) {
1541
+ document.getElementById('noVNC_keyboard_button')
1542
+ .classList.add("noVNC_selected");
1543
+ if (UI.rfb) {
1544
+ UI.rfb.focusOnClick = false;
1545
+ }
1546
+ },
1547
+
1548
+ onblurVirtualKeyboard(event) {
1549
+ document.getElementById('noVNC_keyboard_button')
1550
+ .classList.remove("noVNC_selected");
1551
+ if (UI.rfb) {
1552
+ UI.rfb.focusOnClick = true;
1553
+ }
1554
+ },
1555
+
1556
+ keepVirtualKeyboard(event) {
1557
+ const input = document.getElementById('noVNC_keyboardinput');
1558
+
1559
+ // Only prevent focus change if the virtual keyboard is active
1560
+ if (document.activeElement != input) {
1561
+ return;
1562
+ }
1563
+
1564
+ // Only allow focus to move to other elements that need
1565
+ // focus to function properly
1566
+ if (event.target.form !== undefined) {
1567
+ switch (event.target.type) {
1568
+ case 'text':
1569
+ case 'email':
1570
+ case 'search':
1571
+ case 'password':
1572
+ case 'tel':
1573
+ case 'url':
1574
+ case 'textarea':
1575
+ case 'select-one':
1576
+ case 'select-multiple':
1577
+ return;
1578
+ }
1579
+ }
1580
+
1581
+ event.preventDefault();
1582
+ },
1583
+
1584
+ keyboardinputReset() {
1585
+ const kbi = document.getElementById('noVNC_keyboardinput');
1586
+ kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
1587
+ UI.lastKeyboardinput = kbi.value;
1588
+ },
1589
+
1590
+ keyEvent(keysym, code, down) {
1591
+ if (!UI.rfb) return;
1592
+
1593
+ UI.rfb.sendKey(keysym, code, down);
1594
+ },
1595
+
1596
+ // When normal keyboard events are left uncought, use the input events from
1597
+ // the keyboardinput element instead and generate the corresponding key events.
1598
+ // This code is required since some browsers on Android are inconsistent in
1599
+ // sending keyCodes in the normal keyboard events when using on screen keyboards.
1600
+ keyInput(event) {
1601
+
1602
+ if (!UI.rfb) return;
1603
+
1604
+ const newValue = event.target.value;
1605
+
1606
+ if (!UI.lastKeyboardinput) {
1607
+ UI.keyboardinputReset();
1608
+ }
1609
+ const oldValue = UI.lastKeyboardinput;
1610
+
1611
+ let newLen;
1612
+ try {
1613
+ // Try to check caret position since whitespace at the end
1614
+ // will not be considered by value.length in some browsers
1615
+ newLen = Math.max(event.target.selectionStart, newValue.length);
1616
+ } catch (err) {
1617
+ // selectionStart is undefined in Google Chrome
1618
+ newLen = newValue.length;
1619
+ }
1620
+ const oldLen = oldValue.length;
1621
+
1622
+ let inputs = newLen - oldLen;
1623
+ let backspaces = inputs < 0 ? -inputs : 0;
1624
+
1625
+ // Compare the old string with the new to account for
1626
+ // text-corrections or other input that modify existing text
1627
+ for (let i = 0; i < Math.min(oldLen, newLen); i++) {
1628
+ if (newValue.charAt(i) != oldValue.charAt(i)) {
1629
+ inputs = newLen - i;
1630
+ backspaces = oldLen - i;
1631
+ break;
1632
+ }
1633
+ }
1634
+
1635
+ // Send the key events
1636
+ for (let i = 0; i < backspaces; i++) {
1637
+ UI.rfb.sendKey(KeyTable.XK_BackSpace, "Backspace");
1638
+ }
1639
+ for (let i = newLen - inputs; i < newLen; i++) {
1640
+ UI.rfb.sendKey(keysyms.lookup(newValue.charCodeAt(i)));
1641
+ }
1642
+
1643
+ // Control the text content length in the keyboardinput element
1644
+ if (newLen > 2 * UI.defaultKeyboardinputLen) {
1645
+ UI.keyboardinputReset();
1646
+ } else if (newLen < 1) {
1647
+ // There always have to be some text in the keyboardinput
1648
+ // element with which backspace can interact.
1649
+ UI.keyboardinputReset();
1650
+ // This sometimes causes the keyboard to disappear for a second
1651
+ // but it is required for the android keyboard to recognize that
1652
+ // text has been added to the field
1653
+ event.target.blur();
1654
+ // This has to be ran outside of the input handler in order to work
1655
+ setTimeout(event.target.focus.bind(event.target), 0);
1656
+ } else {
1657
+ UI.lastKeyboardinput = newValue;
1658
+ }
1659
+ },
1660
+
1661
+ /* ------^-------
1662
+ * /KEYBOARD
1663
+ * ==============
1664
+ * EXTRA KEYS
1665
+ * ------v------*/
1666
+
1667
+ openExtraKeys() {
1668
+ UI.closeAllPanels();
1669
+ UI.openControlbar();
1670
+
1671
+ document.getElementById('noVNC_modifiers')
1672
+ .classList.add("noVNC_open");
1673
+ document.getElementById('noVNC_toggle_extra_keys_button')
1674
+ .classList.add("noVNC_selected");
1675
+ },
1676
+
1677
+ closeExtraKeys() {
1678
+ document.getElementById('noVNC_modifiers')
1679
+ .classList.remove("noVNC_open");
1680
+ document.getElementById('noVNC_toggle_extra_keys_button')
1681
+ .classList.remove("noVNC_selected");
1682
+ },
1683
+
1684
+ toggleExtraKeys() {
1685
+ if (document.getElementById('noVNC_modifiers')
1686
+ .classList.contains("noVNC_open")) {
1687
+ UI.closeExtraKeys();
1688
+ } else {
1689
+ UI.openExtraKeys();
1690
+ }
1691
+ },
1692
+
1693
+ sendEsc() {
1694
+ UI.sendKey(KeyTable.XK_Escape, "Escape");
1695
+ },
1696
+
1697
+ sendTab() {
1698
+ UI.sendKey(KeyTable.XK_Tab, "Tab");
1699
+ },
1700
+
1701
+ toggleCtrl() {
1702
+ const btn = document.getElementById('noVNC_toggle_ctrl_button');
1703
+ if (btn.classList.contains("noVNC_selected")) {
1704
+ UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
1705
+ btn.classList.remove("noVNC_selected");
1706
+ } else {
1707
+ UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
1708
+ btn.classList.add("noVNC_selected");
1709
+ }
1710
+ },
1711
+
1712
+ toggleWindows() {
1713
+ const btn = document.getElementById('noVNC_toggle_windows_button');
1714
+ if (btn.classList.contains("noVNC_selected")) {
1715
+ UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", false);
1716
+ btn.classList.remove("noVNC_selected");
1717
+ } else {
1718
+ UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", true);
1719
+ btn.classList.add("noVNC_selected");
1720
+ }
1721
+ },
1722
+
1723
+ toggleAlt() {
1724
+ const btn = document.getElementById('noVNC_toggle_alt_button');
1725
+ if (btn.classList.contains("noVNC_selected")) {
1726
+ UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
1727
+ btn.classList.remove("noVNC_selected");
1728
+ } else {
1729
+ UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
1730
+ btn.classList.add("noVNC_selected");
1731
+ }
1732
+ },
1733
+
1734
+ sendCtrlAltDel() {
1735
+ UI.rfb.sendCtrlAltDel();
1736
+ // See below
1737
+ UI.rfb.focus();
1738
+ UI.idleControlbar();
1739
+ },
1740
+
1741
+ sendKey(keysym, code, down) {
1742
+ UI.rfb.sendKey(keysym, code, down);
1743
+
1744
+ // Move focus to the screen in order to be able to use the
1745
+ // keyboard right after these extra keys.
1746
+ // The exception is when a virtual keyboard is used, because
1747
+ // if we focus the screen the virtual keyboard would be closed.
1748
+ // In this case we focus our special virtual keyboard input
1749
+ // element instead.
1750
+ if (document.getElementById('noVNC_keyboard_button')
1751
+ .classList.contains("noVNC_selected")) {
1752
+ document.getElementById('noVNC_keyboardinput').focus();
1753
+ } else {
1754
+ UI.rfb.focus();
1755
+ }
1756
+ // fade out the controlbar to highlight that
1757
+ // the focus has been moved to the screen
1758
+ UI.idleControlbar();
1759
+ },
1760
+
1761
+ /* ------^-------
1762
+ * /EXTRA KEYS
1763
+ * ==============
1764
+ * MISC
1765
+ * ------v------*/
1766
+
1767
+ updateViewOnly() {
1768
+ if (!UI.rfb) return;
1769
+ UI.rfb.viewOnly = UI.getSetting('view_only');
1770
+
1771
+ UI.updateBeforeUnload();
1772
+
1773
+ // Hide input related buttons in view only mode
1774
+ if (UI.rfb.viewOnly) {
1775
+ document.getElementById('noVNC_keyboard_button')
1776
+ .classList.add('noVNC_hidden');
1777
+ document.getElementById('noVNC_toggle_extra_keys_button')
1778
+ .classList.add('noVNC_hidden');
1779
+ document.getElementById('noVNC_clipboard_button')
1780
+ .classList.add('noVNC_hidden');
1781
+ } else {
1782
+ document.getElementById('noVNC_keyboard_button')
1783
+ .classList.remove('noVNC_hidden');
1784
+ document.getElementById('noVNC_toggle_extra_keys_button')
1785
+ .classList.remove('noVNC_hidden');
1786
+ document.getElementById('noVNC_clipboard_button')
1787
+ .classList.remove('noVNC_hidden');
1788
+ }
1789
+ },
1790
+
1791
+ updateClipboard() {
1792
+ browserAsyncClipboardSupport()
1793
+ .then((support) => {
1794
+ if (support === 'unsupported') {
1795
+ // Use fallback clipboard panel
1796
+ return;
1797
+ }
1798
+ if (support === 'denied' || support === 'available') {
1799
+ UI.closeClipboardPanel();
1800
+ document.getElementById('noVNC_clipboard_button')
1801
+ .classList.add('noVNC_hidden');
1802
+ document.getElementById('noVNC_clipboard_button')
1803
+ .removeEventListener('click', UI.toggleClipboardPanel);
1804
+ document.getElementById('noVNC_clipboard_text')
1805
+ .removeEventListener('change', UI.clipboardSend);
1806
+ if (UI.rfb) {
1807
+ UI.rfb.removeEventListener('clipboard', UI.clipboardReceive);
1808
+ }
1809
+ }
1810
+ })
1811
+ .catch(() => {
1812
+ // Treat as unsupported
1813
+ });
1814
+ },
1815
+
1816
+ updateShowDotCursor() {
1817
+ if (!UI.rfb) return;
1818
+ UI.rfb.showDotCursor = UI.getSetting('show_dot');
1819
+ },
1820
+
1821
+ updateLogging() {
1822
+ WebUtil.initLogging(UI.getSetting('logging'));
1823
+ },
1824
+
1825
+ updateDesktopName(e) {
1826
+ UI.desktopName = e.detail.name;
1827
+ // Display the desktop name in the document title
1828
+ document.title = e.detail.name + " - " + PAGE_TITLE;
1829
+ },
1830
+
1831
+ updateRequestWakelock() {
1832
+ if (!UI.rfb) return;
1833
+ if (UI.getSetting('keep_device_awake')) {
1834
+ UI.wakeLockManager.acquire();
1835
+ } else {
1836
+ UI.wakeLockManager.release();
1837
+ }
1838
+ },
1839
+
1840
+
1841
+ bell(e) {
1842
+ if (UI.getSetting('bell') === 'on') {
1843
+ const promise = document.getElementById('noVNC_bell').play();
1844
+ // The standards disagree on the return value here
1845
+ if (promise) {
1846
+ promise.catch((e) => {
1847
+ if (e.name === "NotAllowedError") {
1848
+ // Ignore when the browser doesn't let us play audio.
1849
+ // It is common that the browsers require audio to be
1850
+ // initiated from a user action.
1851
+ } else {
1852
+ Log.Error("Unable to play bell: " + e);
1853
+ }
1854
+ });
1855
+ }
1856
+ }
1857
+ },
1858
+
1859
+ //Helper to add options to dropdown.
1860
+ addOption(selectbox, text, value) {
1861
+ const optn = document.createElement("OPTION");
1862
+ optn.text = text;
1863
+ optn.value = value;
1864
+ selectbox.options.add(optn);
1865
+ },
1866
+
1867
+ /* ------^-------
1868
+ * /MISC
1869
+ * ==============
1870
+ */
1871
+ };
1872
+
1873
+ export default UI;