ui.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. /*
  2. * noVNC: HTML5 VNC client
  3. * Copyright (C) 2012 Joel Martin
  4. * Copyright (C) 2013 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. "use strict";
  10. /*jslint white: false, browser: true */
  11. /*global window, $D, Util, WebUtil, RFB, Display */
  12. // Load supporting scripts
  13. window.onscriptsload = function () { UI.load(); };
  14. Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
  15. "keysymdef.js", "keyboard.js", "input.js", "display.js",
  16. "jsunzip.js", "rfb.js", "keysym.js"]);
  17. var UI = {
  18. rfb_state : 'loaded',
  19. settingsOpen : false,
  20. connSettingsOpen : false,
  21. popupStatusOpen : false,
  22. clipboardOpen: false,
  23. keyboardVisible: false,
  24. hideKeyboardTimeout: null,
  25. extraKeysVisible: false,
  26. ctrlOn: false,
  27. altOn: false,
  28. isTouchDevice: false,
  29. // Setup rfb object, load settings from browser storage, then call
  30. // UI.init to setup the UI/menus
  31. load: function (callback) {
  32. WebUtil.initSettings(UI.start, callback);
  33. },
  34. // Render default UI and initialize settings menu
  35. start: function(callback) {
  36. var html = '', i, sheet, sheets, llevels, port, autoconnect;
  37. UI.isTouchDevice = 'ontouchstart' in document.documentElement;
  38. // Stylesheet selection dropdown
  39. sheet = WebUtil.selectStylesheet();
  40. sheets = WebUtil.getStylesheets();
  41. for (i = 0; i < sheets.length; i += 1) {
  42. UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
  43. }
  44. // Logging selection dropdown
  45. llevels = ['error', 'warn', 'info', 'debug'];
  46. for (i = 0; i < llevels.length; i += 1) {
  47. UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
  48. }
  49. // Settings with immediate effects
  50. UI.initSetting('logging', 'warn');
  51. WebUtil.init_logging(UI.getSetting('logging'));
  52. UI.initSetting('stylesheet', 'default');
  53. WebUtil.selectStylesheet(null);
  54. // call twice to get around webkit bug
  55. WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
  56. // if port == 80 (or 443) then it won't be present and should be
  57. // set manually
  58. port = window.location.port;
  59. if (!port) {
  60. if (window.location.protocol.substring(0,5) == 'https') {
  61. port = 443;
  62. }
  63. else if (window.location.protocol.substring(0,4) == 'http') {
  64. port = 80;
  65. }
  66. }
  67. /* Populate the controls if defaults are provided in the URL */
  68. UI.initSetting('host', window.location.hostname);
  69. UI.initSetting('port', port);
  70. UI.initSetting('password', '');
  71. UI.initSetting('encrypt', (window.location.protocol === "https:"));
  72. UI.initSetting('true_color', true);
  73. UI.initSetting('cursor', !UI.isTouchDevice);
  74. UI.initSetting('shared', true);
  75. UI.initSetting('view_only', false);
  76. UI.initSetting('path', 'websockify');
  77. UI.initSetting('repeaterID', '');
  78. UI.rfb = RFB({'target': $D('noVNC_canvas'),
  79. 'onUpdateState': UI.updateState,
  80. 'onXvpInit': UI.updateXvpVisualState,
  81. 'onClipboard': UI.clipReceive,
  82. 'onDesktopName': UI.updateDocumentTitle});
  83. autoconnect = WebUtil.getQueryVar('autoconnect', false);
  84. if (autoconnect === 'true' || autoconnect == '1') {
  85. autoconnect = true;
  86. UI.connect();
  87. } else {
  88. autoconnect = false;
  89. }
  90. UI.updateVisualState();
  91. // Unfocus clipboard when over the VNC area
  92. //$D('VNC_screen').onmousemove = function () {
  93. // var keyboard = UI.rfb.get_keyboard();
  94. // if ((! keyboard) || (! keyboard.get_focused())) {
  95. // $D('VNC_clipboard_text').blur();
  96. // }
  97. // };
  98. // Show mouse selector buttons on touch screen devices
  99. if (UI.isTouchDevice) {
  100. // Show mobile buttons
  101. $D('noVNC_mobile_buttons').style.display = "inline";
  102. UI.setMouseButton();
  103. // Remove the address bar
  104. setTimeout(function() { window.scrollTo(0, 1); }, 100);
  105. UI.forceSetting('clip', true);
  106. $D('noVNC_clip').disabled = true;
  107. } else {
  108. UI.initSetting('clip', false);
  109. }
  110. //iOS Safari does not support CSS position:fixed.
  111. //This detects iOS devices and enables javascript workaround.
  112. if ((navigator.userAgent.match(/iPhone/i)) ||
  113. (navigator.userAgent.match(/iPod/i)) ||
  114. (navigator.userAgent.match(/iPad/i))) {
  115. //UI.setOnscroll();
  116. //UI.setResize();
  117. }
  118. UI.setBarPosition();
  119. $D('noVNC_host').focus();
  120. UI.setViewClip();
  121. Util.addEvent(window, 'resize', UI.setViewClip);
  122. Util.addEvent(window, 'beforeunload', function () {
  123. if (UI.rfb_state === 'normal') {
  124. return "You are currently connected.";
  125. }
  126. } );
  127. // Show description by default when hosted at for kanaka.github.com
  128. if (location.host === "kanaka.github.io") {
  129. // Open the description dialog
  130. $D('noVNC_description').style.display = "block";
  131. } else {
  132. // Show the connect panel on first load unless autoconnecting
  133. if (autoconnect === UI.connSettingsOpen) {
  134. UI.toggleConnectPanel();
  135. }
  136. }
  137. // Add mouse event click/focus/blur event handlers to the UI
  138. UI.addMouseHandlers();
  139. if (typeof callback === "function") {
  140. callback(UI.rfb);
  141. }
  142. },
  143. addMouseHandlers: function() {
  144. // Setup interface handlers that can't be inline
  145. $D("noVNC_view_drag_button").onclick = UI.setViewDrag;
  146. $D("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); };
  147. $D("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); };
  148. $D("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); };
  149. $D("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); };
  150. $D("showKeyboard").onclick = UI.showKeyboard;
  151. $D("keyboardinput").oninput = UI.keyInput;
  152. $D("keyboardinput").onblur = UI.keyInputBlur;
  153. $D("showExtraKeysButton").onclick = UI.showExtraKeys;
  154. $D("toggleCtrlButton").onclick = UI.toggleCtrl;
  155. $D("toggleAltButton").onclick = UI.toggleAlt;
  156. $D("sendTabButton").onclick = UI.sendTab;
  157. $D("sendEscButton").onclick = UI.sendEsc;
  158. $D("sendCtrlAltDelButton").onclick = UI.sendCtrlAltDel;
  159. $D("xvpShutdownButton").onclick = UI.xvpShutdown;
  160. $D("xvpRebootButton").onclick = UI.xvpReboot;
  161. $D("xvpResetButton").onclick = UI.xvpReset;
  162. $D("noVNC_status").onclick = UI.togglePopupStatusPanel;
  163. $D("noVNC_popup_status_panel").onclick = UI.togglePopupStatusPanel;
  164. $D("xvpButton").onclick = UI.toggleXvpPanel;
  165. $D("clipboardButton").onclick = UI.toggleClipboardPanel;
  166. $D("settingsButton").onclick = UI.toggleSettingsPanel;
  167. $D("connectButton").onclick = UI.toggleConnectPanel;
  168. $D("disconnectButton").onclick = UI.disconnect;
  169. $D("descriptionButton").onclick = UI.toggleConnectPanel;
  170. $D("noVNC_clipboard_text").onfocus = UI.displayBlur;
  171. $D("noVNC_clipboard_text").onblur = UI.displayFocus;
  172. $D("noVNC_clipboard_text").onchange = UI.clipSend;
  173. $D("noVNC_clipboard_clear_button").onclick = UI.clipClear;
  174. $D("noVNC_settings_menu").onmouseover = UI.displayBlur;
  175. $D("noVNC_settings_menu").onmouseover = UI.displayFocus;
  176. $D("noVNC_apply").onclick = UI.settingsApply;
  177. $D("noVNC_connect_button").onclick = UI.connect;
  178. },
  179. // Read form control compatible setting from cookie
  180. getSetting: function(name) {
  181. var val, ctrl = $D('noVNC_' + name);
  182. val = WebUtil.readSetting(name);
  183. if (val !== null && ctrl.type === 'checkbox') {
  184. if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
  185. val = false;
  186. } else {
  187. val = true;
  188. }
  189. }
  190. return val;
  191. },
  192. // Update cookie and form control setting. If value is not set, then
  193. // updates from control to current cookie setting.
  194. updateSetting: function(name, value) {
  195. var i, ctrl = $D('noVNC_' + name);
  196. // Save the cookie for this session
  197. if (typeof value !== 'undefined') {
  198. WebUtil.writeSetting(name, value);
  199. }
  200. // Update the settings control
  201. value = UI.getSetting(name);
  202. if (ctrl.type === 'checkbox') {
  203. ctrl.checked = value;
  204. } else if (typeof ctrl.options !== 'undefined') {
  205. for (i = 0; i < ctrl.options.length; i += 1) {
  206. if (ctrl.options[i].value === value) {
  207. ctrl.selectedIndex = i;
  208. break;
  209. }
  210. }
  211. } else {
  212. /*Weird IE9 error leads to 'null' appearring
  213. in textboxes instead of ''.*/
  214. if (value === null) {
  215. value = "";
  216. }
  217. ctrl.value = value;
  218. }
  219. },
  220. // Save control setting to cookie
  221. saveSetting: function(name) {
  222. var val, ctrl = $D('noVNC_' + name);
  223. if (ctrl.type === 'checkbox') {
  224. val = ctrl.checked;
  225. } else if (typeof ctrl.options !== 'undefined') {
  226. val = ctrl.options[ctrl.selectedIndex].value;
  227. } else {
  228. val = ctrl.value;
  229. }
  230. WebUtil.writeSetting(name, val);
  231. //Util.Debug("Setting saved '" + name + "=" + val + "'");
  232. return val;
  233. },
  234. // Initial page load read/initialization of settings
  235. initSetting: function(name, defVal) {
  236. var val;
  237. // Check Query string followed by cookie
  238. val = WebUtil.getQueryVar(name);
  239. if (val === null) {
  240. val = WebUtil.readSetting(name, defVal);
  241. }
  242. UI.updateSetting(name, val);
  243. //Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
  244. return val;
  245. },
  246. // Force a setting to be a certain value
  247. forceSetting: function(name, val) {
  248. UI.updateSetting(name, val);
  249. return val;
  250. },
  251. // Show the popup status panel
  252. togglePopupStatusPanel: function() {
  253. var psp = $D('noVNC_popup_status_panel');
  254. if (UI.popupStatusOpen === true) {
  255. psp.style.display = "none";
  256. UI.popupStatusOpen = false;
  257. } else {
  258. psp.innerHTML = $D('noVNC_status').innerHTML;
  259. psp.style.display = "block";
  260. psp.style.left = window.innerWidth/2 -
  261. parseInt(window.getComputedStyle(psp, false).width)/2 -30 + "px";
  262. UI.popupStatusOpen = true;
  263. }
  264. },
  265. // Show the XVP panel
  266. toggleXvpPanel: function() {
  267. // Close the description panel
  268. $D('noVNC_description').style.display = "none";
  269. // Close settings if open
  270. if (UI.settingsOpen === true) {
  271. UI.settingsApply();
  272. UI.closeSettingsMenu();
  273. }
  274. // Close connection settings if open
  275. if (UI.connSettingsOpen === true) {
  276. UI.toggleConnectPanel();
  277. }
  278. // Close popup status panel if open
  279. if (UI.popupStatusOpen === true) {
  280. UI.togglePopupStatusPanel();
  281. }
  282. // Close clipboard panel if open
  283. if (UI.clipboardOpen === true) {
  284. UI.toggleClipboardPanel();
  285. }
  286. // Toggle XVP panel
  287. if (UI.xvpOpen === true) {
  288. $D('noVNC_xvp').style.display = "none";
  289. $D('xvpButton').className = "noVNC_status_button";
  290. UI.xvpOpen = false;
  291. } else {
  292. $D('noVNC_xvp').style.display = "block";
  293. $D('xvpButton').className = "noVNC_status_button_selected";
  294. UI.xvpOpen = true;
  295. }
  296. },
  297. // Show the clipboard panel
  298. toggleClipboardPanel: function() {
  299. // Close the description panel
  300. $D('noVNC_description').style.display = "none";
  301. // Close settings if open
  302. if (UI.settingsOpen === true) {
  303. UI.settingsApply();
  304. UI.closeSettingsMenu();
  305. }
  306. // Close connection settings if open
  307. if (UI.connSettingsOpen === true) {
  308. UI.toggleConnectPanel();
  309. }
  310. // Close popup status panel if open
  311. if (UI.popupStatusOpen === true) {
  312. UI.togglePopupStatusPanel();
  313. }
  314. // Close XVP panel if open
  315. if (UI.xvpOpen === true) {
  316. UI.toggleXvpPanel();
  317. }
  318. // Toggle Clipboard Panel
  319. if (UI.clipboardOpen === true) {
  320. $D('noVNC_clipboard').style.display = "none";
  321. $D('clipboardButton').className = "noVNC_status_button";
  322. UI.clipboardOpen = false;
  323. } else {
  324. $D('noVNC_clipboard').style.display = "block";
  325. $D('clipboardButton').className = "noVNC_status_button_selected";
  326. UI.clipboardOpen = true;
  327. }
  328. },
  329. // Show the connection settings panel/menu
  330. toggleConnectPanel: function() {
  331. // Close the description panel
  332. $D('noVNC_description').style.display = "none";
  333. // Close connection settings if open
  334. if (UI.settingsOpen === true) {
  335. UI.settingsApply();
  336. UI.closeSettingsMenu();
  337. $D('connectButton').className = "noVNC_status_button";
  338. }
  339. // Close clipboard panel if open
  340. if (UI.clipboardOpen === true) {
  341. UI.toggleClipboardPanel();
  342. }
  343. // Close popup status panel if open
  344. if (UI.popupStatusOpen === true) {
  345. UI.togglePopupStatusPanel();
  346. }
  347. // Close XVP panel if open
  348. if (UI.xvpOpen === true) {
  349. UI.toggleXvpPanel();
  350. }
  351. // Toggle Connection Panel
  352. if (UI.connSettingsOpen === true) {
  353. $D('noVNC_controls').style.display = "none";
  354. $D('connectButton').className = "noVNC_status_button";
  355. UI.connSettingsOpen = false;
  356. UI.saveSetting('host');
  357. UI.saveSetting('port');
  358. //UI.saveSetting('password');
  359. } else {
  360. $D('noVNC_controls').style.display = "block";
  361. $D('connectButton').className = "noVNC_status_button_selected";
  362. UI.connSettingsOpen = true;
  363. $D('noVNC_host').focus();
  364. }
  365. },
  366. // Toggle the settings menu:
  367. // On open, settings are refreshed from saved cookies.
  368. // On close, settings are applied
  369. toggleSettingsPanel: function() {
  370. // Close the description panel
  371. $D('noVNC_description').style.display = "none";
  372. if (UI.settingsOpen) {
  373. UI.settingsApply();
  374. UI.closeSettingsMenu();
  375. } else {
  376. UI.updateSetting('encrypt');
  377. UI.updateSetting('true_color');
  378. if (UI.rfb.get_display().get_cursor_uri()) {
  379. UI.updateSetting('cursor');
  380. } else {
  381. UI.updateSetting('cursor', !UI.isTouchDevice);
  382. $D('noVNC_cursor').disabled = true;
  383. }
  384. UI.updateSetting('clip');
  385. UI.updateSetting('shared');
  386. UI.updateSetting('view_only');
  387. UI.updateSetting('path');
  388. UI.updateSetting('repeaterID');
  389. UI.updateSetting('stylesheet');
  390. UI.updateSetting('logging');
  391. UI.openSettingsMenu();
  392. }
  393. },
  394. // Open menu
  395. openSettingsMenu: function() {
  396. // Close the description panel
  397. $D('noVNC_description').style.display = "none";
  398. // Close clipboard panel if open
  399. if (UI.clipboardOpen === true) {
  400. UI.toggleClipboardPanel();
  401. }
  402. // Close connection settings if open
  403. if (UI.connSettingsOpen === true) {
  404. UI.toggleConnectPanel();
  405. }
  406. // Close popup status panel if open
  407. if (UI.popupStatusOpen === true) {
  408. UI.togglePopupStatusPanel();
  409. }
  410. // Close XVP panel if open
  411. if (UI.xvpOpen === true) {
  412. UI.toggleXvpPanel();
  413. }
  414. $D('noVNC_settings').style.display = "block";
  415. $D('settingsButton').className = "noVNC_status_button_selected";
  416. UI.settingsOpen = true;
  417. },
  418. // Close menu (without applying settings)
  419. closeSettingsMenu: function() {
  420. $D('noVNC_settings').style.display = "none";
  421. $D('settingsButton').className = "noVNC_status_button";
  422. UI.settingsOpen = false;
  423. },
  424. // Save/apply settings when 'Apply' button is pressed
  425. settingsApply: function() {
  426. //Util.Debug(">> settingsApply");
  427. UI.saveSetting('encrypt');
  428. UI.saveSetting('true_color');
  429. if (UI.rfb.get_display().get_cursor_uri()) {
  430. UI.saveSetting('cursor');
  431. }
  432. UI.saveSetting('clip');
  433. UI.saveSetting('shared');
  434. UI.saveSetting('view_only');
  435. UI.saveSetting('path');
  436. UI.saveSetting('repeaterID');
  437. UI.saveSetting('stylesheet');
  438. UI.saveSetting('logging');
  439. // Settings with immediate (non-connected related) effect
  440. WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
  441. WebUtil.init_logging(UI.getSetting('logging'));
  442. UI.setViewClip();
  443. UI.setViewDrag(UI.rfb.get_viewportDrag());
  444. //Util.Debug("<< settingsApply");
  445. },
  446. setPassword: function() {
  447. UI.rfb.sendPassword($D('noVNC_password').value);
  448. //Reset connect button.
  449. $D('noVNC_connect_button').value = "Connect";
  450. $D('noVNC_connect_button').onclick = UI.Connect;
  451. //Hide connection panel.
  452. UI.toggleConnectPanel();
  453. return false;
  454. },
  455. sendCtrlAltDel: function() {
  456. UI.rfb.sendCtrlAltDel();
  457. },
  458. xvpShutdown: function() {
  459. UI.rfb.xvpShutdown();
  460. },
  461. xvpReboot: function() {
  462. UI.rfb.xvpReboot();
  463. },
  464. xvpReset: function() {
  465. UI.rfb.xvpReset();
  466. },
  467. setMouseButton: function(num) {
  468. var b, blist = [0, 1,2,4], button;
  469. if (typeof num === 'undefined') {
  470. // Disable mouse buttons
  471. num = -1;
  472. }
  473. if (UI.rfb) {
  474. UI.rfb.get_mouse().set_touchButton(num);
  475. }
  476. for (b = 0; b < blist.length; b++) {
  477. button = $D('noVNC_mouse_button' + blist[b]);
  478. if (blist[b] === num) {
  479. button.style.display = "";
  480. } else {
  481. button.style.display = "none";
  482. /*
  483. button.style.backgroundColor = "black";
  484. button.style.color = "lightgray";
  485. button.style.backgroundColor = "";
  486. button.style.color = "";
  487. */
  488. }
  489. }
  490. },
  491. updateState: function(rfb, state, oldstate, msg) {
  492. var s, sb, c, d, cad, vd, klass;
  493. UI.rfb_state = state;
  494. switch (state) {
  495. case 'failed':
  496. case 'fatal':
  497. klass = "noVNC_status_error";
  498. break;
  499. case 'normal':
  500. klass = "noVNC_status_normal";
  501. break;
  502. case 'disconnected':
  503. $D('noVNC_logo').style.display = "block";
  504. // Fall through
  505. case 'loaded':
  506. klass = "noVNC_status_normal";
  507. break;
  508. case 'password':
  509. UI.toggleConnectPanel();
  510. $D('noVNC_connect_button').value = "Send Password";
  511. $D('noVNC_connect_button').onclick = UI.setPassword;
  512. $D('noVNC_password').focus();
  513. klass = "noVNC_status_warn";
  514. break;
  515. default:
  516. klass = "noVNC_status_warn";
  517. break;
  518. }
  519. if (typeof(msg) !== 'undefined') {
  520. $D('noVNC-control-bar').setAttribute("class", klass);
  521. $D('noVNC_status').innerHTML = msg;
  522. }
  523. UI.updateVisualState();
  524. },
  525. // Disable/enable controls depending on connection state
  526. updateVisualState: function() {
  527. var connected = UI.rfb_state === 'normal' ? true : false;
  528. //Util.Debug(">> updateVisualState");
  529. $D('noVNC_encrypt').disabled = connected;
  530. $D('noVNC_true_color').disabled = connected;
  531. if (UI.rfb && UI.rfb.get_display() &&
  532. UI.rfb.get_display().get_cursor_uri()) {
  533. $D('noVNC_cursor').disabled = connected;
  534. } else {
  535. UI.updateSetting('cursor', !UI.isTouchDevice);
  536. $D('noVNC_cursor').disabled = true;
  537. }
  538. $D('noVNC_shared').disabled = connected;
  539. $D('noVNC_view_only').disabled = connected;
  540. $D('noVNC_path').disabled = connected;
  541. $D('noVNC_repeaterID').disabled = connected;
  542. if (connected) {
  543. UI.setViewClip();
  544. UI.setMouseButton(1);
  545. $D('clipboardButton').style.display = "inline";
  546. $D('showKeyboard').style.display = "inline";
  547. $D('noVNC_extra_keys').style.display = "";
  548. $D('sendCtrlAltDelButton').style.display = "inline";
  549. } else {
  550. UI.setMouseButton();
  551. $D('clipboardButton').style.display = "none";
  552. $D('showKeyboard').style.display = "none";
  553. $D('noVNC_extra_keys').style.display = "none";
  554. $D('sendCtrlAltDelButton').style.display = "none";
  555. UI.updateXvpVisualState(0);
  556. }
  557. // State change disables viewport dragging.
  558. // It is enabled (toggled) by direct click on the button
  559. UI.setViewDrag(false);
  560. switch (UI.rfb_state) {
  561. case 'fatal':
  562. case 'failed':
  563. case 'loaded':
  564. case 'disconnected':
  565. $D('connectButton').style.display = "";
  566. $D('disconnectButton').style.display = "none";
  567. break;
  568. default:
  569. $D('connectButton').style.display = "none";
  570. $D('disconnectButton').style.display = "";
  571. break;
  572. }
  573. //Util.Debug("<< updateVisualState");
  574. },
  575. // Disable/enable XVP button
  576. updateXvpVisualState: function(ver) {
  577. if (ver >= 1) {
  578. $D('xvpButton').style.display = 'inline';
  579. } else {
  580. $D('xvpButton').style.display = 'none';
  581. // Close XVP panel if open
  582. if (UI.xvpOpen === true) {
  583. UI.toggleXvpPanel();
  584. }
  585. }
  586. },
  587. // Display the desktop name in the document title
  588. updateDocumentTitle: function(rfb, name) {
  589. document.title = name + " - noVNC";
  590. },
  591. clipReceive: function(rfb, text) {
  592. Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
  593. $D('noVNC_clipboard_text').value = text;
  594. Util.Debug("<< UI.clipReceive");
  595. },
  596. connect: function() {
  597. var host, port, password, path;
  598. UI.closeSettingsMenu();
  599. UI.toggleConnectPanel();
  600. host = $D('noVNC_host').value;
  601. port = $D('noVNC_port').value;
  602. password = $D('noVNC_password').value;
  603. path = $D('noVNC_path').value;
  604. if ((!host) || (!port)) {
  605. throw("Must set host and port");
  606. }
  607. UI.rfb.set_encrypt(UI.getSetting('encrypt'));
  608. UI.rfb.set_true_color(UI.getSetting('true_color'));
  609. UI.rfb.set_local_cursor(UI.getSetting('cursor'));
  610. UI.rfb.set_shared(UI.getSetting('shared'));
  611. UI.rfb.set_view_only(UI.getSetting('view_only'));
  612. UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
  613. UI.rfb.connect(host, port, password, path);
  614. //Close dialog.
  615. setTimeout(UI.setBarPosition, 100);
  616. $D('noVNC_logo').style.display = "none";
  617. },
  618. disconnect: function() {
  619. UI.closeSettingsMenu();
  620. UI.rfb.disconnect();
  621. $D('noVNC_logo').style.display = "block";
  622. UI.connSettingsOpen = false;
  623. UI.toggleConnectPanel();
  624. },
  625. displayBlur: function() {
  626. UI.rfb.get_keyboard().set_focused(false);
  627. UI.rfb.get_mouse().set_focused(false);
  628. },
  629. displayFocus: function() {
  630. UI.rfb.get_keyboard().set_focused(true);
  631. UI.rfb.get_mouse().set_focused(true);
  632. },
  633. clipClear: function() {
  634. $D('noVNC_clipboard_text').value = "";
  635. UI.rfb.clipboardPasteFrom("");
  636. },
  637. clipSend: function() {
  638. var text = $D('noVNC_clipboard_text').value;
  639. Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
  640. UI.rfb.clipboardPasteFrom(text);
  641. Util.Debug("<< UI.clipSend");
  642. },
  643. // Enable/disable and configure viewport clipping
  644. setViewClip: function(clip) {
  645. var display, cur_clip, pos, new_w, new_h;
  646. if (UI.rfb) {
  647. display = UI.rfb.get_display();
  648. } else {
  649. return;
  650. }
  651. cur_clip = display.get_viewport();
  652. if (typeof(clip) !== 'boolean') {
  653. // Use current setting
  654. clip = UI.getSetting('clip');
  655. }
  656. if (clip && !cur_clip) {
  657. // Turn clipping on
  658. UI.updateSetting('clip', true);
  659. } else if (!clip && cur_clip) {
  660. // Turn clipping off
  661. UI.updateSetting('clip', false);
  662. display.set_viewport(false);
  663. $D('noVNC_canvas').style.position = 'static';
  664. display.viewportChange();
  665. }
  666. if (UI.getSetting('clip')) {
  667. // If clipping, update clipping settings
  668. $D('noVNC_canvas').style.position = 'absolute';
  669. pos = Util.getPosition($D('noVNC_canvas'));
  670. new_w = window.innerWidth - pos.x;
  671. new_h = window.innerHeight - pos.y;
  672. display.set_viewport(true);
  673. display.viewportChange(0, 0, new_w, new_h);
  674. }
  675. },
  676. // Toggle/set/unset the viewport drag/move button
  677. setViewDrag: function(drag) {
  678. var vmb = $D('noVNC_view_drag_button');
  679. if (!UI.rfb) { return; }
  680. if (UI.rfb_state === 'normal' &&
  681. UI.rfb.get_display().get_viewport()) {
  682. vmb.style.display = "inline";
  683. } else {
  684. vmb.style.display = "none";
  685. }
  686. if (typeof(drag) === "undefined" ||
  687. typeof(drag) === "object") {
  688. // If not specified, then toggle
  689. drag = !UI.rfb.get_viewportDrag();
  690. }
  691. if (drag) {
  692. vmb.className = "noVNC_status_button_selected";
  693. UI.rfb.set_viewportDrag(true);
  694. } else {
  695. vmb.className = "noVNC_status_button";
  696. UI.rfb.set_viewportDrag(false);
  697. }
  698. },
  699. // On touch devices, show the OS keyboard
  700. showKeyboard: function() {
  701. var kbi, skb, l;
  702. kbi = $D('keyboardinput');
  703. skb = $D('showKeyboard');
  704. l = kbi.value.length;
  705. if(UI.keyboardVisible === false) {
  706. kbi.focus();
  707. kbi.setSelectionRange(l, l); // Move the caret to the end
  708. UI.keyboardVisible = true;
  709. skb.className = "noVNC_status_button_selected";
  710. } else if(UI.keyboardVisible === true) {
  711. kbi.blur();
  712. skb.className = "noVNC_status_button";
  713. UI.keyboardVisible = false;
  714. }
  715. },
  716. keepKeyboard: function() {
  717. clearTimeout(UI.hideKeyboardTimeout);
  718. if(UI.keyboardVisible === true) {
  719. $D('keyboardinput').focus();
  720. $D('showKeyboard').className = "noVNC_status_button_selected";
  721. } else if(UI.keyboardVisible === false) {
  722. $D('keyboardinput').blur();
  723. $D('showKeyboard').className = "noVNC_status_button";
  724. }
  725. },
  726. // When keypress events are left uncought, catch the input events from
  727. // the keyboardinput element instead and send the corresponding key events.
  728. keyInput: function(event) {
  729. var elem, input, len;
  730. elem = $D('keyboardinput');
  731. input = event.target.value;
  732. len = (elem.selectionStart > input.length) ? elem.selectionStart : input.length;
  733. if (len < 1) { // something removed?
  734. UI.rfb.sendKey(0xff08); // send BACKSPACE
  735. } else if (len > 1) { // new input?
  736. for (var i = len-1; i > 0; i -= 1) {
  737. // HTML does not consider trailing whitespaces as a part of the string
  738. // and they are therefore undefined.
  739. if (input[len-i] !== undefined) {
  740. UI.rfb.sendKey(input.charCodeAt(len-i)); // send charCode
  741. } else {
  742. UI.rfb.sendKey(0x0020); // send SPACE
  743. }
  744. }
  745. }
  746. // In order to be able to delete text which has been written in
  747. // another session there has to always be text in the
  748. // keyboardinput element with which backspace can interact.
  749. // We also need to reset the input field text to avoid overflow.
  750. elem.value = "x";
  751. },
  752. keyInputBlur: function() {
  753. $D('showKeyboard').className = "noVNC_status_button";
  754. //Weird bug in iOS if you change keyboardVisible
  755. //here it does not actually occur so next time
  756. //you click keyboard icon it doesnt work.
  757. UI.hideKeyboardTimeout = setTimeout(function() { UI.setKeyboard(); },100);
  758. },
  759. showExtraKeys: function() {
  760. UI.keepKeyboard();
  761. if(UI.extraKeysVisible === false) {
  762. $D('toggleCtrlButton').style.display = "inline";
  763. $D('toggleAltButton').style.display = "inline";
  764. $D('sendTabButton').style.display = "inline";
  765. $D('sendEscButton').style.display = "inline";
  766. $D('showExtraKeysButton').className = "noVNC_status_button_selected";
  767. UI.extraKeysVisible = true;
  768. } else if(UI.extraKeysVisible === true) {
  769. $D('toggleCtrlButton').style.display = "";
  770. $D('toggleAltButton').style.display = "";
  771. $D('sendTabButton').style.display = "";
  772. $D('sendEscButton').style.display = "";
  773. $D('showExtraKeysButton').className = "noVNC_status_button";
  774. UI.extraKeysVisible = false;
  775. }
  776. },
  777. toggleCtrl: function() {
  778. UI.keepKeyboard();
  779. if(UI.ctrlOn === false) {
  780. UI.rfb.sendKey(XK_Control_L, true);
  781. $D('toggleCtrlButton').className = "noVNC_status_button_selected";
  782. UI.ctrlOn = true;
  783. } else if(UI.ctrlOn === true) {
  784. UI.rfb.sendKey(XK_Control_L, false);
  785. $D('toggleCtrlButton').className = "noVNC_status_button";
  786. UI.ctrlOn = false;
  787. }
  788. },
  789. toggleAlt: function() {
  790. UI.keepKeyboard();
  791. if(UI.altOn === false) {
  792. UI.rfb.sendKey(XK_Alt_L, true);
  793. $D('toggleAltButton').className = "noVNC_status_button_selected";
  794. UI.altOn = true;
  795. } else if(UI.altOn === true) {
  796. UI.rfb.sendKey(XK_Alt_L, false);
  797. $D('toggleAltButton').className = "noVNC_status_button";
  798. UI.altOn = false;
  799. }
  800. },
  801. sendTab: function() {
  802. UI.keepKeyboard();
  803. UI.rfb.sendKey(XK_Tab);
  804. },
  805. sendEsc: function() {
  806. UI.keepKeyboard();
  807. UI.rfb.sendKey(XK_Escape);
  808. },
  809. setKeyboard: function() {
  810. UI.keyboardVisible = false;
  811. },
  812. // iOS < Version 5 does not support position fixed. Javascript workaround:
  813. setOnscroll: function() {
  814. window.onscroll = function() {
  815. UI.setBarPosition();
  816. };
  817. },
  818. setResize: function () {
  819. window.onResize = function() {
  820. UI.setBarPosition();
  821. };
  822. },
  823. //Helper to add options to dropdown.
  824. addOption: function(selectbox,text,value )
  825. {
  826. var optn = document.createElement("OPTION");
  827. optn.text = text;
  828. optn.value = value;
  829. selectbox.options.add(optn);
  830. },
  831. setBarPosition: function() {
  832. $D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px';
  833. $D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
  834. var vncwidth = $D('noVNC_screen').style.offsetWidth;
  835. $D('noVNC-control-bar').style.width = vncwidth + 'px';
  836. }
  837. };