ui.js 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293
  1. /*
  2. * noVNC: HTML5 VNC client
  3. * Copyright (C) 2012 Joel Martin
  4. * Copyright (C) 2016 Samuel Mannehed for Cendio AB
  5. * Licensed under MPL 2.0 (see LICENSE.txt)
  6. *
  7. * See README.md for usage and integration instructions.
  8. */
  9. /* jslint white: false, browser: true */
  10. /* global window, $D, Util, WebUtil, RFB, Display */
  11. var UI;
  12. (function () {
  13. "use strict";
  14. // Load supporting scripts
  15. window.onscriptsload = function () { UI.load(); };
  16. Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
  17. "keysymdef.js", "keyboard.js", "input.js", "display.js",
  18. "rfb.js", "keysym.js", "inflator.js"]);
  19. window.setInterval( function() {
  20. if (UI.rfb_state === 'disconnected') {
  21. UI.connect()
  22. }
  23. }, 15000);
  24. UI = {
  25. rfb_state: 'loaded',
  26. resizeTimeout: null,
  27. popupStatusTimeout: null,
  28. hideKeyboardTimeout: null,
  29. settingsOpen: false,
  30. connSettingsOpen: false,
  31. clipboardOpen: false,
  32. keyboardVisible: false,
  33. isTouchDevice: false,
  34. isSafari: false,
  35. rememberedClipSetting: null,
  36. lastKeyboardinput: null,
  37. defaultKeyboardinputLen: 100,
  38. shiftDown: false,
  39. ctrlDown: false,
  40. altDown: false,
  41. altGrDown: false,
  42. // Setup rfb object, load settings from browser storage, then call
  43. // UI.init to setup the UI/menus
  44. load: function(callback) {
  45. WebUtil.initSettings(UI.start, callback);
  46. },
  47. // Render default UI and initialize settings menu
  48. start: function(callback) {
  49. UI.isTouchDevice = 'ontouchstart' in document.documentElement;
  50. // Stylesheet selection dropdown
  51. var sheet = WebUtil.selectStylesheet();
  52. var sheets = WebUtil.getStylesheets();
  53. var i;
  54. for (i = 0; i < sheets.length; i += 1) {
  55. UI.addOption($D('noVNC_setting_stylesheet'),sheets[i].title, sheets[i].title);
  56. }
  57. // Logging selection dropdown
  58. var llevels = ['error', 'warn', 'info', 'debug'];
  59. for (i = 0; i < llevels.length; i += 1) {
  60. UI.addOption($D('noVNC_setting_logging'),llevels[i], llevels[i]);
  61. }
  62. // Settings with immediate effects
  63. UI.initSetting('logging', 'warn');
  64. WebUtil.init_logging(UI.getSetting('logging'));
  65. UI.initSetting('stylesheet', 'default');
  66. WebUtil.selectStylesheet(null);
  67. // call twice to get around webkit bug
  68. WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
  69. // if port == 80 (or 443) then it won't be present and should be
  70. // set manually
  71. var port = window.location.port;
  72. if (!port) {
  73. if (window.location.protocol.substring(0,5) == 'https') {
  74. port = 443;
  75. }
  76. else if (window.location.protocol.substring(0,4) == 'http') {
  77. port = 80;
  78. }
  79. }
  80. /* Populate the controls if defaults are provided in the URL */
  81. UI.initSetting('host', window.location.hostname);
  82. UI.initSetting('port', port);
  83. UI.initSetting('password', '');
  84. UI.initSetting('encrypt', (window.location.protocol === "https:"));
  85. UI.initSetting('true_color', true);
  86. UI.initSetting('cursor', !UI.isTouchDevice);
  87. UI.initSetting('resize', 'off');
  88. UI.initSetting('shared', true);
  89. UI.initSetting('view_only', false);
  90. UI.initSetting('path', 'websockify');
  91. UI.initSetting('repeaterID', '');
  92. UI.initSetting('token', '');
  93. var autoconnect = WebUtil.getConfigVar('autoconnect', false);
  94. if (autoconnect === 'true' || autoconnect == '1') {
  95. autoconnect = true;
  96. UI.connect();
  97. } else {
  98. autoconnect = false;
  99. }
  100. UI.updateVisualState();
  101. $D('noVNC_setting_host').focus();
  102. // Show mouse selector buttons on touch screen devices
  103. if (UI.isTouchDevice) {
  104. // Show mobile buttons
  105. $D('noVNC_mobile_buttons').style.display = "inline";
  106. UI.setMouseButton();
  107. // Remove the address bar
  108. setTimeout(function() { window.scrollTo(0, 1); }, 100);
  109. UI.forceSetting('clip', true);
  110. } else {
  111. UI.initSetting('clip', false);
  112. }
  113. UI.setViewClip();
  114. UI.setBarPosition();
  115. Util.addEvent(window, 'resize', function () {
  116. UI.applyResizeMode();
  117. UI.setViewClip();
  118. UI.updateViewDrag();
  119. UI.setBarPosition();
  120. } );
  121. UI.isSafari = (navigator.userAgent.indexOf('Safari') != -1 &&
  122. navigator.userAgent.indexOf('Chrome') == -1);
  123. // Only show the button if fullscreen is properly supported
  124. // * Safari doesn't support alphanumerical input while in fullscreen
  125. if (!UI.isSafari &&
  126. (document.documentElement.requestFullscreen ||
  127. document.documentElement.mozRequestFullScreen ||
  128. document.documentElement.webkitRequestFullscreen ||
  129. document.body.msRequestFullscreen)) {
  130. $D('noVNC_fullscreen_button').style.display = "inline";
  131. Util.addEvent(window, 'fullscreenchange', UI.updateFullscreenButton);
  132. Util.addEvent(window, 'mozfullscreenchange', UI.updateFullscreenButton);
  133. Util.addEvent(window, 'webkitfullscreenchange', UI.updateFullscreenButton);
  134. Util.addEvent(window, 'msfullscreenchange', UI.updateFullscreenButton);
  135. }
  136. Util.addEvent(window, 'load', UI.keyboardinputReset);
  137. Util.addEvent(window, 'beforeunload', function () {
  138. if (UI.rfb && UI.rfb_state === 'normal') {
  139. return "You are currently connected.";
  140. }
  141. } );
  142. // Show description by default when hosted at for kanaka.github.com
  143. if (location.host === "kanaka.github.io") {
  144. // Open the description dialog
  145. $D('noVNC_description').style.display = "block";
  146. } else {
  147. // Show the connect panel on first load unless autoconnecting
  148. if (autoconnect === UI.connSettingsOpen) {
  149. UI.toggleConnectPanel();
  150. }
  151. }
  152. // Add mouse event click/focus/blur event handlers to the UI
  153. UI.addMouseHandlers();
  154. if (typeof callback === "function") {
  155. callback(UI.rfb);
  156. }
  157. },
  158. initRFB: function() {
  159. try {
  160. UI.rfb = new RFB({'target': $D('noVNC_canvas'),
  161. 'onUpdateState': UI.updateState,
  162. 'onXvpInit': UI.updateXvpButton,
  163. 'onClipboard': UI.clipboardReceive,
  164. 'onFBUComplete': UI.initialResize,
  165. 'onFBResize': UI.updateViewDrag,
  166. 'onDesktopName': UI.updateDocumentTitle});
  167. return true;
  168. } catch (exc) {
  169. UI.updateState(null, 'fatal', null, 'Unable to create RFB client -- ' + exc);
  170. return false;
  171. }
  172. },
  173. addMouseHandlers: function() {
  174. // Setup interface handlers that can't be inline
  175. $D("noVNC_view_drag_button").onclick = UI.toggleViewDrag;
  176. $D("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); };
  177. $D("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); };
  178. $D("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); };
  179. $D("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); };
  180. $D("noVNC_keyboard_button").onclick = UI.showKeyboard;
  181. $D("noVNC_keyboardinput").oninput = UI.keyInput;
  182. $D("noVNC_keyboardinput").onblur = UI.hideKeyboard;
  183. $D("noVNC_keyboardinput").onsubmit = function () { return false; };
  184. $D("noVNC_toggleExtraKeys_button").onclick = UI.toggleExtraKeys;
  185. $D("noVNC_toggleCtrl_button").onclick = UI.toggleCtrl;
  186. $D("noVNC_toggleAlt_button").onclick = UI.toggleAlt;
  187. $D("noVNC_sendTab_button").onclick = UI.sendTab;
  188. $D("noVNC_sendEsc_button").onclick = UI.sendEsc;
  189. $D("noVNC_sendCtrlAltDel_button").onclick = UI.sendCtrlAltDel;
  190. $D("noVNC_xvpShutdown_button").onclick = function() { UI.rfb.xvpShutdown(); },
  191. $D("noVNC_xvpReboot_button").onclick = function() { UI.rfb.xvpReboot(); },
  192. $D("noVNC_xvpReset_button").onclick = function() { UI.rfb.xvpReset(); },
  193. $D("noVNC_status").onclick = UI.popupStatus;
  194. $D("noVNC_popup_status").onclick = UI.closePopup;
  195. $D("noVNC_toggleXvp_button").onclick = UI.toggleXvpPanel;
  196. $D("noVNC_clipboard_button").onclick = UI.toggleClipboardPanel;
  197. $D("noVNC_fullscreen_button").onclick = UI.toggleFullscreen;
  198. $D("noVNC_settings_button").onclick = UI.toggleSettingsPanel;
  199. $D("noVNC_connectPanel_button").onclick = UI.toggleConnectPanel;
  200. $D("noVNC_disconnect_button").onclick = UI.disconnect;
  201. $D("noVNC_description_button").onclick = UI.toggleConnectPanel;
  202. $D("noVNC_clipboard_text").onfocus = UI.displayBlur;
  203. $D("noVNC_clipboard_text").onblur = UI.displayFocus;
  204. $D("noVNC_clipboard_text").onchange = UI.clipboardSend;
  205. $D("noVNC_clipboard_clear_button").onclick = UI.clipboardClear;
  206. $D("noVNC_settings_menu").onmouseover = UI.displayBlur;
  207. $D("noVNC_settings_menu").onmouseover = UI.displayFocus;
  208. $D("noVNC_settings_apply").onclick = UI.settingsApply;
  209. $D("noVNC_connect_button").onclick = UI.connect;
  210. $D("noVNC_setting_resize").onchange = UI.enableDisableViewClip;
  211. },
  212. /* ------^-------
  213. * /INIT
  214. * ==============
  215. * VISUAL
  216. * ------v------*/
  217. updateState: function(rfb, state, oldstate, msg) {
  218. UI.rfb_state = state;
  219. var klass;
  220. switch (state) {
  221. case 'failed':
  222. case 'fatal':
  223. klass = "noVNC_status_error";
  224. break;
  225. case 'normal':
  226. klass = "noVNC_status_normal";
  227. break;
  228. case 'disconnected':
  229. $D('noVNC_logo').style.display = "block";
  230. $D('noVNC_screen').style.display = "none";
  231. /* falls through */
  232. case 'loaded':
  233. klass = "noVNC_status_normal";
  234. break;
  235. case 'password':
  236. UI.toggleConnectPanel();
  237. $D('noVNC_connect_button').value = "Send Password";
  238. $D('noVNC_connect_button').onclick = UI.setPassword;
  239. $D('noVNC_setting_password').focus();
  240. klass = "noVNC_status_warn";
  241. break;
  242. default:
  243. klass = "noVNC_status_warn";
  244. break;
  245. }
  246. if (typeof(msg) !== 'undefined') {
  247. $D('noVNC_control_bar').setAttribute("class", klass);
  248. $D('noVNC_status').textContent = msg;
  249. }
  250. UI.updateVisualState();
  251. },
  252. // Disable/enable controls depending on connection state
  253. updateVisualState: function() {
  254. var connected = UI.rfb && UI.rfb_state === 'normal';
  255. //Util.Debug(">> updateVisualState");
  256. $D('noVNC_setting_encrypt').disabled = connected;
  257. $D('noVNC_setting_true_color').disabled = connected;
  258. if (Util.browserSupportsCursorURIs()) {
  259. $D('noVNC_setting_cursor').disabled = connected;
  260. } else {
  261. UI.updateSetting('cursor', !UI.isTouchDevice);
  262. $D('noVNC_setting_cursor').disabled = true;
  263. }
  264. UI.enableDisableViewClip();
  265. $D('noVNC_setting_resize').disabled = connected;
  266. $D('noVNC_setting_shared').disabled = connected;
  267. $D('noVNC_setting_view_only').disabled = connected;
  268. $D('noVNC_setting_path').disabled = connected;
  269. $D('noVNC_setting_repeaterID').disabled = connected;
  270. if (connected) {
  271. UI.setViewClip();
  272. UI.setMouseButton(1);
  273. $D('noVNC_clipboard_button').style.display = "inline";
  274. $D('noVNC_keyboard_button').style.display = "inline";
  275. $D('noVNC_extra_keys').style.display = "";
  276. $D('noVNC_sendCtrlAltDel_button').style.display = "inline";
  277. } else {
  278. UI.setMouseButton();
  279. $D('noVNC_clipboard_button').style.display = "none";
  280. $D('noVNC_keyboard_button').style.display = "none";
  281. $D('noVNC_extra_keys').style.display = "none";
  282. $D('noVNC_sendCtrlAltDel_button').style.display = "none";
  283. UI.updateXvpButton(0);
  284. }
  285. // State change disables viewport dragging.
  286. // It is enabled (toggled) by direct click on the button
  287. UI.updateViewDrag(false);
  288. switch (UI.rfb_state) {
  289. case 'fatal':
  290. case 'failed':
  291. case 'disconnected':
  292. $D('noVNC_connectPanel_button').style.display = "";
  293. $D('noVNC_disconnect_button').style.display = "none";
  294. UI.connSettingsOpen = false;
  295. UI.toggleConnectPanel();
  296. break;
  297. case 'loaded':
  298. $D('noVNC_connectPanel_button').style.display = "";
  299. $D('noVNC_disconnect_button').style.display = "none";
  300. break;
  301. default:
  302. $D('noVNC_connectPanel_button').style.display = "none";
  303. $D('noVNC_disconnect_button').style.display = "";
  304. break;
  305. }
  306. //Util.Debug("<< updateVisualState");
  307. },
  308. popupStatus: function(text) {
  309. var psp = $D('noVNC_popup_status');
  310. clearTimeout(UI.popupStatusTimeout);
  311. if (typeof text === 'string') {
  312. psp.textContent = text;
  313. } else {
  314. psp.textContent = $D('noVNC_status').textContent;
  315. }
  316. psp.style.display = "block";
  317. psp.style.left = window.innerWidth/2 -
  318. parseInt(window.getComputedStyle(psp).width)/2 -30 + "px";
  319. // Show the popup for a maximum of 1.5 seconds
  320. UI.popupStatusTimeout = setTimeout(UI.closePopup, 1500);
  321. },
  322. closePopup: function() {
  323. clearTimeout(UI.popupStatusTimeout);
  324. $D('noVNC_popup_status').style.display = "none";
  325. },
  326. /* ------^-------
  327. * /VISUAL
  328. * ==============
  329. * SETTINGS
  330. * ------v------*/
  331. // Initial page load read/initialization of settings
  332. initSetting: function(name, defVal) {
  333. // Check Query string followed by cookie
  334. var val = WebUtil.getConfigVar(name);
  335. if (val === null) {
  336. val = WebUtil.readSetting(name, defVal);
  337. }
  338. UI.updateSetting(name, val);
  339. return val;
  340. },
  341. // Update cookie and form control setting. If value is not set, then
  342. // updates from control to current cookie setting.
  343. updateSetting: function(name, value) {
  344. // Save the cookie for this session
  345. if (typeof value !== 'undefined') {
  346. WebUtil.writeSetting(name, value);
  347. }
  348. // Update the settings control
  349. value = UI.getSetting(name);
  350. var ctrl = $D('noVNC_setting_' + name);
  351. if (ctrl.type === 'checkbox') {
  352. ctrl.checked = value;
  353. } else if (typeof ctrl.options !== 'undefined') {
  354. for (var i = 0; i < ctrl.options.length; i += 1) {
  355. if (ctrl.options[i].value === value) {
  356. ctrl.selectedIndex = i;
  357. break;
  358. }
  359. }
  360. } else {
  361. /*Weird IE9 error leads to 'null' appearring
  362. in textboxes instead of ''.*/
  363. if (value === null) {
  364. value = "";
  365. }
  366. ctrl.value = value;
  367. }
  368. },
  369. // Save control setting to cookie
  370. saveSetting: function(name) {
  371. var val, ctrl = $D('noVNC_setting_' + name);
  372. if (ctrl.type === 'checkbox') {
  373. val = ctrl.checked;
  374. } else if (typeof ctrl.options !== 'undefined') {
  375. val = ctrl.options[ctrl.selectedIndex].value;
  376. } else {
  377. val = ctrl.value;
  378. }
  379. WebUtil.writeSetting(name, val);
  380. //Util.Debug("Setting saved '" + name + "=" + val + "'");
  381. return val;
  382. },
  383. // Force a setting to be a certain value
  384. forceSetting: function(name, val) {
  385. UI.updateSetting(name, val);
  386. return val;
  387. },
  388. // Read form control compatible setting from cookie
  389. getSetting: function(name) {
  390. var ctrl = $D('noVNC_setting_' + name);
  391. var val = WebUtil.readSetting(name);
  392. if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') {
  393. if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
  394. val = false;
  395. } else {
  396. val = true;
  397. }
  398. }
  399. return val;
  400. },
  401. // Save/apply settings when 'Apply' button is pressed
  402. settingsApply: function() {
  403. //Util.Debug(">> settingsApply");
  404. UI.saveSetting('encrypt');
  405. UI.saveSetting('true_color');
  406. if (Util.browserSupportsCursorURIs()) {
  407. UI.saveSetting('cursor');
  408. }
  409. UI.saveSetting('resize');
  410. if (UI.getSetting('resize') === 'downscale' || UI.getSetting('resize') === 'scale') {
  411. UI.forceSetting('clip', false);
  412. }
  413. UI.saveSetting('clip');
  414. UI.saveSetting('shared');
  415. UI.saveSetting('view_only');
  416. UI.saveSetting('path');
  417. UI.saveSetting('repeaterID');
  418. UI.saveSetting('stylesheet');
  419. UI.saveSetting('logging');
  420. // Settings with immediate (non-connected related) effect
  421. WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
  422. WebUtil.init_logging(UI.getSetting('logging'));
  423. UI.setViewClip();
  424. UI.updateViewDrag();
  425. //Util.Debug("<< settingsApply");
  426. },
  427. // Open menu
  428. openSettingsMenu: function() {
  429. // Close the description panel
  430. $D('noVNC_description').style.display = "none";
  431. // Close clipboard panel if open
  432. if (UI.clipboardOpen === true) {
  433. UI.toggleClipboardPanel();
  434. }
  435. // Close connection settings if open
  436. if (UI.connSettingsOpen === true) {
  437. UI.toggleConnectPanel();
  438. }
  439. // Close XVP panel if open
  440. if (UI.xvpOpen === true) {
  441. UI.toggleXvpPanel();
  442. }
  443. $D('noVNC_settings').style.display = "block";
  444. $D('noVNC_settings_button').className = "noVNC_status_button_selected";
  445. UI.settingsOpen = true;
  446. },
  447. // Close menu (without applying settings)
  448. closeSettingsMenu: function() {
  449. $D('noVNC_settings').style.display = "none";
  450. $D('noVNC_settings_button').className = "noVNC_status_button";
  451. UI.settingsOpen = false;
  452. },
  453. // Toggle the settings menu:
  454. // On open, settings are refreshed from saved cookies.
  455. // On close, settings are applied
  456. toggleSettingsPanel: function() {
  457. // Close the description panel
  458. $D('noVNC_description').style.display = "none";
  459. if (UI.settingsOpen) {
  460. UI.settingsApply();
  461. UI.closeSettingsMenu();
  462. } else {
  463. UI.updateSetting('encrypt');
  464. UI.updateSetting('true_color');
  465. if (Util.browserSupportsCursorURIs()) {
  466. UI.updateSetting('cursor');
  467. } else {
  468. UI.updateSetting('cursor', !UI.isTouchDevice);
  469. $D('noVNC_setting_cursor').disabled = true;
  470. }
  471. UI.updateSetting('clip');
  472. UI.updateSetting('resize');
  473. UI.updateSetting('shared');
  474. UI.updateSetting('view_only');
  475. UI.updateSetting('path');
  476. UI.updateSetting('repeaterID');
  477. UI.updateSetting('stylesheet');
  478. UI.updateSetting('logging');
  479. UI.openSettingsMenu();
  480. }
  481. },
  482. /* ------^-------
  483. * /SETTINGS
  484. * ==============
  485. * XVP
  486. * ------v------*/
  487. // Show the XVP panel
  488. toggleXvpPanel: function() {
  489. // Close the description panel
  490. $D('noVNC_description').style.display = "none";
  491. // Close settings if open
  492. if (UI.settingsOpen === true) {
  493. UI.settingsApply();
  494. UI.closeSettingsMenu();
  495. }
  496. // Close connection settings if open
  497. if (UI.connSettingsOpen === true) {
  498. UI.toggleConnectPanel();
  499. }
  500. // Close clipboard panel if open
  501. if (UI.clipboardOpen === true) {
  502. UI.toggleClipboardPanel();
  503. }
  504. // Toggle XVP panel
  505. if (UI.xvpOpen === true) {
  506. $D('noVNC_xvp').style.display = "none";
  507. $D('noVNC_toggleXvp_button').className = "noVNC_status_button";
  508. UI.xvpOpen = false;
  509. } else {
  510. $D('noVNC_xvp').style.display = "block";
  511. $D('noVNC_toggleXvp_button').className = "noVNC_status_button_selected";
  512. UI.xvpOpen = true;
  513. }
  514. },
  515. // Disable/enable XVP button
  516. updateXvpButton: function(ver) {
  517. if (ver >= 1) {
  518. $D('noVNC_toggleXvp_button').style.display = 'inline';
  519. } else {
  520. $D('noVNC_toggleXvp_button').style.display = 'none';
  521. // Close XVP panel if open
  522. if (UI.xvpOpen === true) {
  523. UI.toggleXvpPanel();
  524. }
  525. }
  526. },
  527. /* ------^-------
  528. * /XVP
  529. * ==============
  530. * CLIPBOARD
  531. * ------v------*/
  532. // Show the clipboard panel
  533. toggleClipboardPanel: function() {
  534. // Close the description panel
  535. $D('noVNC_description').style.display = "none";
  536. // Close settings if open
  537. if (UI.settingsOpen === true) {
  538. UI.settingsApply();
  539. UI.closeSettingsMenu();
  540. }
  541. // Close connection settings if open
  542. if (UI.connSettingsOpen === true) {
  543. UI.toggleConnectPanel();
  544. }
  545. // Close XVP panel if open
  546. if (UI.xvpOpen === true) {
  547. UI.toggleXvpPanel();
  548. }
  549. // Toggle Clipboard Panel
  550. if (UI.clipboardOpen === true) {
  551. $D('noVNC_clipboard').style.display = "none";
  552. $D('noVNC_clipboard_button').className = "noVNC_status_button";
  553. UI.clipboardOpen = false;
  554. } else {
  555. $D('noVNC_clipboard').style.display = "block";
  556. $D('noVNC_clipboard_button').className = "noVNC_status_button_selected";
  557. UI.clipboardOpen = true;
  558. }
  559. },
  560. clipboardReceive: function(rfb, text) {
  561. Util.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
  562. $D('noVNC_clipboard_text').value = text;
  563. Util.Debug("<< UI.clipboardReceive");
  564. },
  565. clipboardClear: function() {
  566. $D('noVNC_clipboard_text').value = "";
  567. UI.rfb.clipboardPasteFrom("");
  568. },
  569. clipboardSend: function() {
  570. var text = $D('noVNC_clipboard_text').value;
  571. Util.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
  572. UI.rfb.clipboardPasteFrom(text);
  573. Util.Debug("<< UI.clipboardSend");
  574. },
  575. /* ------^-------
  576. * /CLIPBOARD
  577. * ==============
  578. * CONNECTION
  579. * ------v------*/
  580. // Show the connection settings panel/menu
  581. toggleConnectPanel: function() {
  582. // Close the description panel
  583. $D('noVNC_description').style.display = "none";
  584. // Close connection settings if open
  585. if (UI.settingsOpen === true) {
  586. UI.settingsApply();
  587. UI.closeSettingsMenu();
  588. $D('noVNC_connectPanel_button').className = "noVNC_status_button";
  589. }
  590. // Close clipboard panel if open
  591. if (UI.clipboardOpen === true) {
  592. UI.toggleClipboardPanel();
  593. }
  594. // Close XVP panel if open
  595. if (UI.xvpOpen === true) {
  596. UI.toggleXvpPanel();
  597. }
  598. // Toggle Connection Panel
  599. if (UI.connSettingsOpen === true) {
  600. $D('noVNC_controls').style.display = "none";
  601. $D('noVNC_connectPanel_button').className = "noVNC_status_button";
  602. UI.connSettingsOpen = false;
  603. UI.saveSetting('host');
  604. UI.saveSetting('port');
  605. UI.saveSetting('token');
  606. //UI.saveSetting('password');
  607. } else {
  608. $D('noVNC_controls').style.display = "block";
  609. $D('noVNC_connectPanel_button').className = "noVNC_status_button_selected";
  610. UI.connSettingsOpen = true;
  611. $D('noVNC_setting_host').focus();
  612. }
  613. },
  614. connect: function() {
  615. UI.closeSettingsMenu();
  616. UI.toggleConnectPanel();
  617. var host = $D('noVNC_setting_host').value;
  618. var port = $D('noVNC_setting_port').value;
  619. var password = $D('noVNC_setting_password').value;
  620. var token = $D('noVNC_setting_token').value;
  621. var path = $D('noVNC_setting_path').value;
  622. //if token is in path then ignore the new token variable
  623. if (token) {
  624. path = WebUtil.injectParamIfMissing(path, "token", token);
  625. }
  626. if ((!host) || (!port)) {
  627. throw new Error("Must set host and port");
  628. }
  629. if (!UI.initRFB()) return;
  630. UI.rfb.set_encrypt(UI.getSetting('encrypt'));
  631. UI.rfb.set_true_color(UI.getSetting('true_color'));
  632. UI.rfb.set_local_cursor(UI.getSetting('cursor'));
  633. UI.rfb.set_shared(UI.getSetting('shared'));
  634. UI.rfb.set_view_only(UI.getSetting('view_only'));
  635. UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
  636. UI.rfb.connect(host, port, password, path);
  637. //Close dialog.
  638. setTimeout(UI.setBarPosition, 100);
  639. $D('noVNC_logo').style.display = "none";
  640. $D('noVNC_screen').style.display = "inline";
  641. },
  642. disconnect: function() {
  643. UI.closeSettingsMenu();
  644. UI.rfb.disconnect();
  645. // Restore the callback used for initial resize
  646. UI.rfb.set_onFBUComplete(UI.initialResize);
  647. $D('noVNC_logo').style.display = "block";
  648. $D('noVNC_screen').style.display = "none";
  649. // Don't display the connection settings until we're actually disconnected
  650. },
  651. setPassword: function() {
  652. UI.rfb.sendPassword($D('noVNC_setting_password').value);
  653. //Reset connect button.
  654. $D('noVNC_connect_button').value = "Connect";
  655. $D('noVNC_connect_button').onclick = UI.connect;
  656. //Hide connection panel.
  657. UI.toggleConnectPanel();
  658. return false;
  659. },
  660. /* ------^-------
  661. * /CONNECTION
  662. * ==============
  663. * FULLSCREEN
  664. * ------v------*/
  665. toggleFullscreen: function() {
  666. if (document.fullscreenElement || // alternative standard method
  667. document.mozFullScreenElement || // currently working methods
  668. document.webkitFullscreenElement ||
  669. document.msFullscreenElement) {
  670. if (document.exitFullscreen) {
  671. document.exitFullscreen();
  672. } else if (document.mozCancelFullScreen) {
  673. document.mozCancelFullScreen();
  674. } else if (document.webkitExitFullscreen) {
  675. document.webkitExitFullscreen();
  676. } else if (document.msExitFullscreen) {
  677. document.msExitFullscreen();
  678. }
  679. } else {
  680. if (document.documentElement.requestFullscreen) {
  681. document.documentElement.requestFullscreen();
  682. } else if (document.documentElement.mozRequestFullScreen) {
  683. document.documentElement.mozRequestFullScreen();
  684. } else if (document.documentElement.webkitRequestFullscreen) {
  685. document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  686. } else if (document.body.msRequestFullscreen) {
  687. document.body.msRequestFullscreen();
  688. }
  689. }
  690. UI.enableDisableViewClip();
  691. UI.updateFullscreenButton();
  692. },
  693. updateFullscreenButton: function() {
  694. if (document.fullscreenElement || // alternative standard method
  695. document.mozFullScreenElement || // currently working methods
  696. document.webkitFullscreenElement ||
  697. document.msFullscreenElement ) {
  698. $D('noVNC_fullscreen_button').className = "noVNC_status_button_selected";
  699. } else {
  700. $D('noVNC_fullscreen_button').className = "noVNC_status_button";
  701. }
  702. },
  703. /* ------^-------
  704. * /FULLSCREEN
  705. * ==============
  706. * RESIZE
  707. * ------v------*/
  708. // Apply remote resizing or local scaling
  709. applyResizeMode: function() {
  710. if (!UI.rfb) return;
  711. var screen = UI.screenSize();
  712. if (screen && UI.rfb_state === 'normal' && UI.rfb.get_display()) {
  713. var display = UI.rfb.get_display();
  714. var resizeMode = UI.getSetting('resize');
  715. if (resizeMode === 'remote') {
  716. // Request changing the resolution of the remote display to
  717. // the size of the local browser viewport.
  718. // In order to not send multiple requests before the browser-resize
  719. // is finished we wait 0.5 seconds before sending the request.
  720. clearTimeout(UI.resizeTimeout);
  721. UI.resizeTimeout = setTimeout(function(){
  722. // Limit the viewport to the size of the browser window
  723. display.set_maxWidth(screen.w);
  724. display.set_maxHeight(screen.h);
  725. Util.Debug('Attempting requestDesktopSize(' +
  726. screen.w + ', ' + screen.h + ')');
  727. // Request a remote size covering the viewport
  728. UI.rfb.requestDesktopSize(screen.w, screen.h);
  729. }, 500);
  730. } else if (resizeMode === 'scale' || resizeMode === 'downscale') {
  731. var downscaleOnly = resizeMode === 'downscale';
  732. var scaleRatio = display.autoscale(screen.w, screen.h, downscaleOnly);
  733. UI.rfb.get_mouse().set_scale(scaleRatio);
  734. Util.Debug('Scaling by ' + UI.rfb.get_mouse().get_scale());
  735. }
  736. }
  737. },
  738. // The screen is always the same size as the available viewport
  739. // in the browser window minus the height of the control bar
  740. screenSize: function() {
  741. var screen = $D('noVNC_screen');
  742. // Hide the scrollbars until the size is calculated
  743. screen.style.overflow = "hidden";
  744. var pos = Util.getPosition(screen);
  745. var w = pos.width;
  746. var h = pos.height;
  747. screen.style.overflow = "visible";
  748. if (isNaN(w) || isNaN(h)) {
  749. return false;
  750. } else {
  751. return {w: w, h: h};
  752. }
  753. },
  754. // Normally we only apply the current resize mode after a window resize
  755. // event. This means that when a new connection is opened, there is no
  756. // resize mode active.
  757. // We have to wait until the first FBU because this is where the client
  758. // will find the supported encodings of the server. Some calls later in
  759. // the chain is dependant on knowing the server-capabilities.
  760. initialResize: function(rfb, fbu) {
  761. UI.applyResizeMode();
  762. // After doing this once, we remove the callback.
  763. UI.rfb.set_onFBUComplete(function() { });
  764. },
  765. /* ------^-------
  766. * /RESIZE
  767. * ==============
  768. * CLIPPING
  769. * ------v------*/
  770. // Set and configure viewport clipping
  771. setViewClip: function(clip) {
  772. var display;
  773. if (UI.rfb) {
  774. display = UI.rfb.get_display();
  775. } else {
  776. UI.forceSetting('clip', clip);
  777. return;
  778. }
  779. var cur_clip = display.get_viewport();
  780. if (typeof(clip) !== 'boolean') {
  781. // Use current setting
  782. clip = UI.getSetting('clip');
  783. }
  784. if (clip && !cur_clip) {
  785. // Turn clipping on
  786. UI.updateSetting('clip', true);
  787. } else if (!clip && cur_clip) {
  788. // Turn clipping off
  789. UI.updateSetting('clip', false);
  790. display.set_viewport(false);
  791. // Disable max dimensions
  792. display.set_maxWidth(0);
  793. display.set_maxHeight(0);
  794. display.viewportChangeSize();
  795. }
  796. if (UI.getSetting('clip')) {
  797. // If clipping, update clipping settings
  798. display.set_viewport(true);
  799. var size = UI.screenSize();
  800. if (size) {
  801. display.set_maxWidth(size.w);
  802. display.set_maxHeight(size.h);
  803. // Hide potential scrollbars that can skew the position
  804. $D('noVNC_screen').style.overflow = "hidden";
  805. // The x position marks the left margin of the canvas,
  806. // remove the margin from both sides to keep it centered
  807. var new_w = size.w - (2 * Util.getPosition($D('noVNC_canvas')).x);
  808. $D('noVNC_screen').style.overflow = "visible";
  809. display.viewportChangeSize(new_w, size.h);
  810. }
  811. }
  812. },
  813. // Handle special cases where clipping is forced on/off or locked
  814. enableDisableViewClip: function() {
  815. var resizeSetting = $D('noVNC_setting_resize');
  816. var connected = UI.rfb && UI.rfb_state === 'normal';
  817. if (UI.isSafari) {
  818. // Safari auto-hides the scrollbars which makes them
  819. // impossible to use in most cases
  820. UI.setViewClip(true);
  821. $D('noVNC_setting_clip').disabled = true;
  822. } else if (resizeSetting.value === 'downscale' || resizeSetting.value === 'scale') {
  823. // Disable clipping if we are scaling
  824. UI.setViewClip(false);
  825. $D('noVNC_setting_clip').disabled = true;
  826. } else if (document.msFullscreenElement) {
  827. // The browser is IE and we are in fullscreen mode.
  828. // - We need to force clipping while in fullscreen since
  829. // scrollbars doesn't work.
  830. UI.popupStatus("Forcing clipping mode since scrollbars aren't supported by IE in fullscreen");
  831. UI.rememberedClipSetting = UI.getSetting('clip');
  832. UI.setViewClip(true);
  833. $D('noVNC_setting_clip').disabled = true;
  834. } else if (document.body.msRequestFullscreen && UI.rememberedClip !== null) {
  835. // Restore view clip to what it was before fullscreen on IE
  836. UI.setViewClip(UI.rememberedClipSetting);
  837. $D('noVNC_setting_clip').disabled = connected || UI.isTouchDevice;
  838. } else {
  839. $D('noVNC_setting_clip').disabled = connected || UI.isTouchDevice;
  840. if (UI.isTouchDevice) {
  841. UI.setViewClip(true);
  842. }
  843. }
  844. },
  845. /* ------^-------
  846. * /CLIPPING
  847. * ==============
  848. * VIEWDRAG
  849. * ------v------*/
  850. // Update the viewport drag state
  851. updateViewDrag: function(drag) {
  852. if (!UI.rfb) return;
  853. var viewDragButton = $D('noVNC_view_drag_button');
  854. // Check if viewport drag is possible. It is only possible
  855. // if the remote display is clipping the client display.
  856. if (UI.rfb_state === 'normal' &&
  857. UI.rfb.get_display().get_viewport() &&
  858. UI.rfb.get_display().clippingDisplay()) {
  859. viewDragButton.style.display = "inline";
  860. viewDragButton.disabled = false;
  861. } else {
  862. // The size of the remote display is the same or smaller
  863. // than the client display. Make sure viewport drag isn't
  864. // active when it can't be used.
  865. if (UI.rfb.get_viewportDrag) {
  866. viewDragButton.className = "noVNC_status_button";
  867. UI.rfb.set_viewportDrag(false);
  868. }
  869. // The button is disabled instead of hidden on touch devices
  870. if (UI.rfb_state === 'normal' && UI.isTouchDevice) {
  871. viewDragButton.style.display = "inline";
  872. viewDragButton.disabled = true;
  873. } else {
  874. viewDragButton.style.display = "none";
  875. }
  876. return;
  877. }
  878. if (typeof(drag) !== "undefined" &&
  879. typeof(drag) !== "object") {
  880. if (drag) {
  881. viewDragButton.className = "noVNC_status_button_selected";
  882. UI.rfb.set_viewportDrag(true);
  883. } else {
  884. viewDragButton.className = "noVNC_status_button";
  885. UI.rfb.set_viewportDrag(false);
  886. }
  887. }
  888. },
  889. toggleViewDrag: function() {
  890. if (!UI.rfb) return;
  891. var viewDragButton = $D('noVNC_view_drag_button');
  892. if (UI.rfb.get_viewportDrag()) {
  893. viewDragButton.className = "noVNC_status_button";
  894. UI.rfb.set_viewportDrag(false);
  895. } else {
  896. viewDragButton.className = "noVNC_status_button_selected";
  897. UI.rfb.set_viewportDrag(true);
  898. }
  899. },
  900. /* ------^-------
  901. * /VIEWDRAG
  902. * ==============
  903. * KEYBOARD
  904. * ------v------*/
  905. // On touch devices, show the OS keyboard
  906. showKeyboard: function() {
  907. var kbi = $D('noVNC_keyboardinput');
  908. var skb = $D('noVNC_keyboard_button');
  909. var l = kbi.value.length;
  910. if(UI.keyboardVisible === false) {
  911. kbi.focus();
  912. try { kbi.setSelectionRange(l, l); } // Move the caret to the end
  913. catch (err) {} // setSelectionRange is undefined in Google Chrome
  914. UI.keyboardVisible = true;
  915. skb.className = "noVNC_status_button_selected";
  916. } else if(UI.keyboardVisible === true) {
  917. kbi.blur();
  918. skb.className = "noVNC_status_button";
  919. UI.keyboardVisible = false;
  920. }
  921. },
  922. hideKeyboard: function() {
  923. $D('noVNC_keyboard_button').className = "noVNC_status_button";
  924. //Weird bug in iOS if you change keyboardVisible
  925. //here it does not actually occur so next time
  926. //you click keyboard icon it doesnt work.
  927. UI.hideKeyboardTimeout = setTimeout(function() {
  928. UI.keyboardVisible = false;
  929. },100);
  930. },
  931. keepKeyboard: function() {
  932. clearTimeout(UI.hideKeyboardTimeout);
  933. if(UI.keyboardVisible === true) {
  934. $D('noVNC_keyboardinput').focus();
  935. $D('noVNC_keyboard_button').className = "noVNC_status_button_selected";
  936. } else if(UI.keyboardVisible === false) {
  937. $D('noVNC_keyboardinput').blur();
  938. $D('noVNC_keyboard_button').className = "noVNC_status_button";
  939. }
  940. },
  941. keyboardinputReset: function() {
  942. var kbi = $D('noVNC_keyboardinput');
  943. kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
  944. UI.lastKeyboardinput = kbi.value;
  945. },
  946. // When normal keyboard events are left uncought, use the input events from
  947. // the keyboardinput element instead and generate the corresponding key events.
  948. // This code is required since some browsers on Android are inconsistent in
  949. // sending keyCodes in the normal keyboard events when using on screen keyboards.
  950. keyInput: function(event) {
  951. if (!UI.rfb) return;
  952. var newValue = event.target.value;
  953. if (!UI.lastKeyboardinput) {
  954. UI.keyboardinputReset();
  955. }
  956. var oldValue = UI.lastKeyboardinput;
  957. var newLen;
  958. try {
  959. // Try to check caret position since whitespace at the end
  960. // will not be considered by value.length in some browsers
  961. newLen = Math.max(event.target.selectionStart, newValue.length);
  962. } catch (err) {
  963. // selectionStart is undefined in Google Chrome
  964. newLen = newValue.length;
  965. }
  966. var oldLen = oldValue.length;
  967. var backspaces;
  968. var inputs = newLen - oldLen;
  969. if (inputs < 0) {
  970. backspaces = -inputs;
  971. } else {
  972. backspaces = 0;
  973. }
  974. // Compare the old string with the new to account for
  975. // text-corrections or other input that modify existing text
  976. var i;
  977. for (i = 0; i < Math.min(oldLen, newLen); i++) {
  978. if (newValue.charAt(i) != oldValue.charAt(i)) {
  979. inputs = newLen - i;
  980. backspaces = oldLen - i;
  981. break;
  982. }
  983. }
  984. // Send the key events
  985. for (i = 0; i < backspaces; i++) {
  986. UI.rfb.sendKey(XK_BackSpace);
  987. }
  988. for (i = newLen - inputs; i < newLen; i++) {
  989. UI.rfb.sendKey(newValue.charCodeAt(i));
  990. }
  991. // Control the text content length in the keyboardinput element
  992. if (newLen > 2 * UI.defaultKeyboardinputLen) {
  993. UI.keyboardinputReset();
  994. } else if (newLen < 1) {
  995. // There always have to be some text in the keyboardinput
  996. // element with which backspace can interact.
  997. UI.keyboardinputReset();
  998. // This sometimes causes the keyboard to disappear for a second
  999. // but it is required for the android keyboard to recognize that
  1000. // text has been added to the field
  1001. event.target.blur();
  1002. // This has to be ran outside of the input handler in order to work
  1003. setTimeout(UI.keepKeyboard, 0);
  1004. } else {
  1005. UI.lastKeyboardinput = newValue;
  1006. }
  1007. },
  1008. toggleExtraKeys: function() {
  1009. UI.keepKeyboard();
  1010. if(UI.extraKeysVisible === false) {
  1011. $D('noVNC_toggleCtrl_button').style.display = "inline";
  1012. $D('noVNC_toggleAlt_button').style.display = "inline";
  1013. $D('noVNC_sendTab_button').style.display = "inline";
  1014. $D('noVNC_sendEsc_button').style.display = "inline";
  1015. $D('noVNC_toggleExtraKeys_button').className = "noVNC_status_button_selected";
  1016. UI.extraKeysVisible = true;
  1017. } else if(UI.extraKeysVisible === true) {
  1018. $D('noVNC_toggleCtrl_button').style.display = "";
  1019. $D('noVNC_toggleAlt_button').style.display = "";
  1020. $D('noVNC_sendTab_button').style.display = "";
  1021. $D('noVNC_sendEsc_button').style.display = "";
  1022. $D('noVNC_toggleExtraKeys_button').className = "noVNC_status_button";
  1023. UI.extraKeysVisible = false;
  1024. }
  1025. },
  1026. sendEsc: function() {
  1027. UI.keepKeyboard();
  1028. UI.rfb.sendKey(XK_Escape);
  1029. },
  1030. sendTab: function() {
  1031. UI.keepKeyboard();
  1032. UI.rfb.sendKey(XK_Tab);
  1033. },
  1034. toggleCtrl: function() {
  1035. UI.keepKeyboard();
  1036. if(UI.ctrlOn === false) {
  1037. UI.rfb.sendKey(XK_Control_L, true);
  1038. $D('noVNC_toggleCtrl_button').className = "noVNC_status_button_selected";
  1039. UI.ctrlOn = true;
  1040. } else if(UI.ctrlOn === true) {
  1041. UI.rfb.sendKey(XK_Control_L, false);
  1042. $D('noVNC_toggleCtrl_button').className = "noVNC_status_button";
  1043. UI.ctrlOn = false;
  1044. }
  1045. },
  1046. toggleAlt: function() {
  1047. UI.keepKeyboard();
  1048. if(UI.altOn === false) {
  1049. UI.rfb.sendKey(XK_Alt_L, true);
  1050. $D('noVNC_toggleAlt_button').className = "noVNC_status_button_selected";
  1051. UI.altOn = true;
  1052. } else if(UI.altOn === true) {
  1053. UI.rfb.sendKey(XK_Alt_L, false);
  1054. $D('noVNC_toggleAlt_button').className = "noVNC_status_button";
  1055. UI.altOn = false;
  1056. }
  1057. },
  1058. sendCtrlAltDel: function() {
  1059. UI.rfb.sendCtrlAltDel();
  1060. },
  1061. /* ------^-------
  1062. * /KEYBOARD
  1063. * ==============
  1064. * MISC
  1065. * ------v------*/
  1066. setMouseButton: function(num) {
  1067. if (typeof num === 'undefined') {
  1068. // Disable mouse buttons
  1069. num = -1;
  1070. }
  1071. if (UI.rfb) {
  1072. UI.rfb.get_mouse().set_touchButton(num);
  1073. }
  1074. var blist = [0, 1,2,4];
  1075. for (var b = 0; b < blist.length; b++) {
  1076. var button = $D('noVNC_mouse_button' + blist[b]);
  1077. if (blist[b] === num) {
  1078. button.style.display = "";
  1079. } else {
  1080. button.style.display = "none";
  1081. }
  1082. }
  1083. },
  1084. displayBlur: function() {
  1085. if (!UI.rfb) return;
  1086. UI.rfb.get_keyboard().set_focused(false);
  1087. UI.rfb.get_mouse().set_focused(false);
  1088. },
  1089. displayFocus: function() {
  1090. if (!UI.rfb) return;
  1091. UI.rfb.get_keyboard().set_focused(true);
  1092. UI.rfb.get_mouse().set_focused(true);
  1093. },
  1094. // Display the desktop name in the document title
  1095. updateDocumentTitle: function(rfb, name) {
  1096. document.title = name + " - noVNC";
  1097. },
  1098. //Helper to add options to dropdown.
  1099. addOption: function(selectbox, text, value) {
  1100. var optn = document.createElement("OPTION");
  1101. optn.text = text;
  1102. optn.value = value;
  1103. selectbox.options.add(optn);
  1104. },
  1105. setBarPosition: function() {
  1106. $D('noVNC_control_bar').style.top = (window.pageYOffset) + 'px';
  1107. $D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
  1108. var vncwidth = $D('noVNC_container').style.offsetWidth;
  1109. $D('noVNC_control_bar').style.width = vncwidth + 'px';
  1110. }
  1111. /* ------^-------
  1112. * /MISC
  1113. * ==============
  1114. */
  1115. };
  1116. })();