ui.js 24 KB

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