ui.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. /*
  2. * noVNC: HTML5 VNC client
  3. * Copyright (C) 2011 Joel Martin
  4. * Licensed under LGPL-3 (see LICENSE.txt)
  5. *
  6. * See README.md for usage and integration instructions.
  7. */
  8. "use strict";
  9. /*jslint white: false, browser: true */
  10. /*global window, $D, Util, WebUtil, RFB, Display */
  11. var UI = {
  12. rfb_state : 'loaded',
  13. settingsOpen : false,
  14. connSettingsOpen : true,
  15. clipboardOpen: false,
  16. keyboardVisible: false,
  17. // Render default UI and initialize settings menu
  18. load: function() {
  19. var html = '', i, sheet, sheets, llevels;
  20. // Stylesheet selection dropdown
  21. sheet = WebUtil.selectStylesheet();
  22. sheets = WebUtil.getStylesheets();
  23. for (i = 0; i < sheets.length; i += 1) {
  24. UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
  25. }
  26. // Logging selection dropdown
  27. llevels = ['error', 'warn', 'info', 'debug'];
  28. for (i = 0; i < llevels.length; i += 1) {
  29. UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
  30. }
  31. // Settings with immediate effects
  32. UI.initSetting('logging', 'warn');
  33. WebUtil.init_logging(UI.getSetting('logging'));
  34. UI.initSetting('stylesheet', 'default');
  35. WebUtil.selectStylesheet(null);
  36. // call twice to get around webkit bug
  37. WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
  38. /* Populate the controls if defaults are provided in the URL */
  39. UI.initSetting('host', '');
  40. UI.initSetting('port', '');
  41. UI.initSetting('password', '');
  42. UI.initSetting('encrypt', false);
  43. UI.initSetting('true_color', true);
  44. UI.initSetting('cursor', false);
  45. UI.initSetting('shared', true);
  46. UI.initSetting('view_only', false);
  47. UI.initSetting('connectTimeout', 2);
  48. UI.initSetting('path', '');
  49. UI.rfb = RFB({'target': $D('noVNC_canvas'),
  50. 'onUpdateState': UI.updateState,
  51. 'onClipboard': UI.clipReceive});
  52. UI.updateVisualState();
  53. // Unfocus clipboard when over the VNC area
  54. //$D('VNC_screen').onmousemove = function () {
  55. // var keyboard = UI.rfb.get_keyboard();
  56. // if ((! keyboard) || (! keyboard.get_focused())) {
  57. // $D('VNC_clipboard_text').blur();
  58. // }
  59. // };
  60. // Show mouse selector buttons on touch screen devices
  61. if ('ontouchstart' in document.documentElement) {
  62. // Show mobile buttons
  63. $D('noVNC_mobile_buttons').style.display = "inline";
  64. UI.setMouseButton();
  65. // Remove the address bar
  66. setTimeout(function() { window.scrollTo(0, 1); }, 100);
  67. UI.forceSetting('clip', true);
  68. $D('noVNC_clip').disabled = true;
  69. } else {
  70. UI.initSetting('clip', false);
  71. }
  72. //iOS Safari does not support CSS position:fixed.
  73. //This detects iOS devices and enables javascript workaround.
  74. if ((navigator.userAgent.match(/iPhone/i)) ||
  75. (navigator.userAgent.match(/iPod/i)) ||
  76. (navigator.userAgent.match(/iPad/i))) {
  77. //UI.setOnscroll();
  78. //UI.setResize();
  79. }
  80. $D('noVNC_host').focus();
  81. UI.setViewClip();
  82. Util.addEvent(window, 'resize', UI.setViewClip);
  83. Util.addEvent(window, 'beforeunload', function () {
  84. if (UI.rfb_state === 'normal') {
  85. return "You are currently connected.";
  86. }
  87. } );
  88. },
  89. // Read form control compatible setting from cookie
  90. getSetting: function(name) {
  91. var val, ctrl = $D('noVNC_' + name);
  92. val = WebUtil.readCookie(name);
  93. if (ctrl.type === 'checkbox') {
  94. if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) {
  95. val = false;
  96. } else {
  97. val = true;
  98. }
  99. }
  100. return val;
  101. },
  102. // Update cookie and form control setting. If value is not set, then
  103. // updates from control to current cookie setting.
  104. updateSetting: function(name, value) {
  105. var i, ctrl = $D('noVNC_' + name);
  106. // Save the cookie for this session
  107. if (typeof value !== 'undefined') {
  108. WebUtil.createCookie(name, value);
  109. }
  110. // Update the settings control
  111. value = UI.getSetting(name);
  112. if (ctrl.type === 'checkbox') {
  113. ctrl.checked = value;
  114. } else if (typeof ctrl.options !== 'undefined') {
  115. for (i = 0; i < ctrl.options.length; i += 1) {
  116. if (ctrl.options[i].value === value) {
  117. ctrl.selectedIndex = i;
  118. break;
  119. }
  120. }
  121. } else {
  122. /*Weird IE9 error leads to 'null' appearring
  123. in textboxes instead of ''.*/
  124. if (value === null) {
  125. value = "";
  126. }
  127. ctrl.value = value;
  128. }
  129. },
  130. // Save control setting to cookie
  131. saveSetting: function(name) {
  132. var val, ctrl = $D('noVNC_' + name);
  133. if (ctrl.type === 'checkbox') {
  134. val = ctrl.checked;
  135. } else if (typeof ctrl.options !== 'undefined') {
  136. val = ctrl.options[ctrl.selectedIndex].value;
  137. } else {
  138. val = ctrl.value;
  139. }
  140. WebUtil.createCookie(name, val);
  141. //Util.Debug("Setting saved '" + name + "=" + val + "'");
  142. return val;
  143. },
  144. // Initial page load read/initialization of settings
  145. initSetting: function(name, defVal) {
  146. var val;
  147. // Check Query string followed by cookie
  148. val = WebUtil.getQueryVar(name);
  149. if (val === null) {
  150. val = WebUtil.readCookie(name, defVal);
  151. }
  152. UI.updateSetting(name, val);
  153. //Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
  154. return val;
  155. },
  156. // Force a setting to be a certain value
  157. forceSetting: function(name, val) {
  158. UI.updateSetting(name, val);
  159. return val;
  160. },
  161. // Show the clipboard panel
  162. toggleClipboardPanel: function() {
  163. //Close settings if open
  164. if (UI.settingsOpen == true) {
  165. UI.settingsApply();
  166. UI.closeSettingsMenu();
  167. }
  168. //Close connection settings if open
  169. if (UI.connSettingsOpen == true) {
  170. UI.toggleConnectPanel();
  171. }
  172. //Toggle Clipboard Panel
  173. if (UI.clipboardOpen == true) {
  174. $D('noVNC_clipboard').style.display = "none";
  175. $D('clipboardButton').className = "noVNC_status_button";
  176. UI.clipboardOpen = false;
  177. } else {
  178. $D('noVNC_clipboard').style.display = "block";
  179. $D('clipboardButton').className = "noVNC_status_button_selected";
  180. UI.clipboardOpen = true;
  181. }
  182. },
  183. // Show the connection settings panel/menu
  184. toggleConnectPanel: function() {
  185. //Close connection settings if open
  186. if (UI.settingsOpen == true) {
  187. UI.settingsApply();
  188. UI.closeSettingsMenu();
  189. $D('connectButton').className = "noVNC_status_button";
  190. }
  191. if (UI.clipboardOpen == true) {
  192. UI.toggleClipboardPanel();
  193. }
  194. //Toggle Connection Panel
  195. if (UI.connSettingsOpen == true) {
  196. $D('noVNC_controls').style.display = "none";
  197. $D('connectButton').className = "noVNC_status_button";
  198. UI.connSettingsOpen = false;
  199. } else {
  200. $D('noVNC_controls').style.display = "block";
  201. $D('connectButton').className = "noVNC_status_button_selected";
  202. UI.connSettingsOpen = true;
  203. $D('noVNC_host').focus();
  204. }
  205. },
  206. // Toggle the settings menu:
  207. // On open, settings are refreshed from saved cookies.
  208. // On close, settings are applied
  209. toggleSettingsPanel: function() {
  210. if (UI.settingsOpen) {
  211. UI.settingsApply();
  212. UI.closeSettingsMenu();
  213. } else {
  214. UI.updateSetting('encrypt');
  215. UI.updateSetting('true_color');
  216. if (UI.rfb.get_display().get_cursor_uri()) {
  217. UI.updateSetting('cursor');
  218. } else {
  219. UI.updateSetting('cursor', false);
  220. $D('noVNC_cursor').disabled = true;
  221. }
  222. UI.updateSetting('clip');
  223. UI.updateSetting('shared');
  224. UI.updateSetting('view_only');
  225. UI.updateSetting('connectTimeout');
  226. UI.updateSetting('path');
  227. UI.updateSetting('stylesheet');
  228. UI.updateSetting('logging');
  229. UI.openSettingsMenu();
  230. }
  231. },
  232. // Open menu
  233. openSettingsMenu: function() {
  234. if (UI.clipboardOpen == true) {
  235. UI.toggleClipboardPanel();
  236. }
  237. //Close connection settings if open
  238. if (UI.connSettingsOpen == true) {
  239. UI.toggleConnectPanel();
  240. }
  241. $D('noVNC_settings').style.display = "block";
  242. $D('settingsButton').className = "noVNC_status_button_selected";
  243. UI.settingsOpen = true;
  244. },
  245. // Close menu (without applying settings)
  246. closeSettingsMenu: function() {
  247. $D('noVNC_settings').style.display = "none";
  248. $D('settingsButton').className = "noVNC_status_button";
  249. UI.settingsOpen = false;
  250. },
  251. // Save/apply settings when 'Apply' button is pressed
  252. settingsApply: function() {
  253. //Util.Debug(">> settingsApply");
  254. UI.saveSetting('encrypt');
  255. UI.saveSetting('true_color');
  256. if (UI.rfb.get_display().get_cursor_uri()) {
  257. UI.saveSetting('cursor');
  258. }
  259. UI.saveSetting('clip');
  260. UI.saveSetting('shared');
  261. UI.saveSetting('view_only');
  262. UI.saveSetting('connectTimeout');
  263. UI.saveSetting('path');
  264. UI.saveSetting('stylesheet');
  265. UI.saveSetting('logging');
  266. // Settings with immediate (non-connected related) effect
  267. WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
  268. WebUtil.init_logging(UI.getSetting('logging'));
  269. UI.setViewClip();
  270. UI.setViewDrag(UI.rfb.get_viewportDrag());
  271. //Util.Debug("<< settingsApply");
  272. },
  273. setPassword: function() {
  274. UI.rfb.sendPassword($D('noVNC_password').value);
  275. //Reset connect button.
  276. $D('noVNC_connect_button').value = "Connect";
  277. $D('noVNC_connect_button').onclick = UI.Connect;
  278. //Hide connection panel.
  279. UI.toggleConnectPanel();
  280. return false;
  281. },
  282. sendCtrlAltDel: function() {
  283. UI.rfb.sendCtrlAltDel();
  284. },
  285. setMouseButton: function(num) {
  286. var b, blist = [0, 1,2,4], button;
  287. if (typeof num === 'undefined') {
  288. // Disable mouse buttons
  289. num = -1;
  290. }
  291. if (UI.rfb) {
  292. UI.rfb.get_mouse().set_touchButton(num);
  293. }
  294. for (b = 0; b < blist.length; b++) {
  295. button = $D('noVNC_mouse_button' + blist[b]);
  296. if (blist[b] === num) {
  297. button.style.display = "";
  298. } else {
  299. button.style.display = "none";
  300. /*
  301. button.style.backgroundColor = "black";
  302. button.style.color = "lightgray";
  303. button.style.backgroundColor = "";
  304. button.style.color = "";
  305. */
  306. }
  307. }
  308. },
  309. updateState: function(rfb, state, oldstate, msg) {
  310. var s, sb, c, d, cad, vd, klass;
  311. UI.rfb_state = state;
  312. s = $D('noVNC_status');
  313. sb = $D('noVNC_status_bar');
  314. switch (state) {
  315. case 'failed':
  316. case 'fatal':
  317. klass = "noVNC_status_error";
  318. break;
  319. case 'normal':
  320. klass = "noVNC_status_normal";
  321. break;
  322. case 'disconnected':
  323. $D('noVNC_logo').style.display = "block";
  324. case 'loaded':
  325. klass = "noVNC_status_normal";
  326. break;
  327. case 'password':
  328. UI.toggleConnectPanel();
  329. $D('noVNC_connect_button').value = "Send Password";
  330. $D('noVNC_connect_button').onclick = UI.setPassword;
  331. $D('noVNC_password').focus();
  332. klass = "noVNC_status_warn";
  333. break;
  334. default:
  335. klass = "noVNC_status_warn";
  336. break;
  337. }
  338. if (typeof(msg) !== 'undefined') {
  339. s.setAttribute("class", klass);
  340. sb.setAttribute("class", klass);
  341. s.innerHTML = msg;
  342. }
  343. UI.updateVisualState();
  344. },
  345. // Disable/enable controls depending on connection state
  346. updateVisualState: function() {
  347. var connected = UI.rfb_state === 'normal' ? true : false;
  348. //Util.Debug(">> updateVisualState");
  349. $D('noVNC_encrypt').disabled = connected;
  350. $D('noVNC_true_color').disabled = connected;
  351. if (UI.rfb && UI.rfb.get_display() &&
  352. UI.rfb.get_display().get_cursor_uri()) {
  353. $D('noVNC_cursor').disabled = connected;
  354. } else {
  355. UI.updateSetting('cursor', false);
  356. $D('noVNC_cursor').disabled = true;
  357. }
  358. $D('noVNC_shared').disabled = connected;
  359. $D('noVNC_view_only').disabled = connected;
  360. $D('noVNC_connectTimeout').disabled = connected;
  361. $D('noVNC_path').disabled = connected;
  362. if (connected) {
  363. UI.setViewClip();
  364. UI.setMouseButton(1);
  365. $D('showKeyboard').style.display = "inline";
  366. $D('sendCtrlAltDelButton').style.display = "inline";
  367. } else {
  368. UI.setMouseButton();
  369. $D('showKeyboard').style.display = "none";
  370. $D('sendCtrlAltDelButton').style.display = "none";
  371. }
  372. // State change disables viewport dragging.
  373. // It is enabled (toggled) by direct click on the button
  374. UI.setViewDrag(false);
  375. switch (UI.rfb_state) {
  376. case 'fatal':
  377. case 'failed':
  378. case 'loaded':
  379. case 'disconnected':
  380. $D('connectButton').style.display = "";
  381. $D('disconnectButton').style.display = "none";
  382. break;
  383. default:
  384. $D('connectButton').style.display = "none";
  385. $D('disconnectButton').style.display = "";
  386. break;
  387. }
  388. //Util.Debug("<< updateVisualState");
  389. },
  390. clipReceive: function(rfb, text) {
  391. Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
  392. $D('noVNC_clipboard_text').value = text;
  393. Util.Debug("<< UI.clipReceive");
  394. },
  395. connect: function() {
  396. var host, port, password, path;
  397. UI.closeSettingsMenu();
  398. UI.toggleConnectPanel();
  399. host = $D('noVNC_host').value;
  400. port = $D('noVNC_port').value;
  401. password = $D('noVNC_password').value;
  402. path = $D('noVNC_path').value;
  403. if ((!host) || (!port)) {
  404. throw("Must set host and port");
  405. }
  406. UI.rfb.set_encrypt(UI.getSetting('encrypt'));
  407. UI.rfb.set_true_color(UI.getSetting('true_color'));
  408. UI.rfb.set_local_cursor(UI.getSetting('cursor'));
  409. UI.rfb.set_shared(UI.getSetting('shared'));
  410. UI.rfb.set_view_only(UI.getSetting('view_only'));
  411. UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout'));
  412. UI.rfb.connect(host, port, password, path);
  413. //Close dialog.
  414. setTimeout(UI.setBarPosition, 100);
  415. $D('noVNC_logo').style.display = "none";
  416. },
  417. disconnect: function() {
  418. UI.closeSettingsMenu();
  419. UI.rfb.disconnect();
  420. $D('noVNC_logo').style.display = "block";
  421. UI.connSettingsOpen = false;
  422. UI.toggleConnectPanel();
  423. },
  424. displayBlur: function() {
  425. UI.rfb.get_keyboard().set_focused(false);
  426. UI.rfb.get_mouse().set_focused(false);
  427. },
  428. displayFocus: function() {
  429. UI.rfb.get_keyboard().set_focused(true);
  430. UI.rfb.get_mouse().set_focused(true);
  431. },
  432. clipClear: function() {
  433. $D('noVNC_clipboard_text').value = "";
  434. UI.rfb.clipboardPasteFrom("");
  435. },
  436. clipSend: function() {
  437. var text = $D('noVNC_clipboard_text').value;
  438. Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
  439. UI.rfb.clipboardPasteFrom(text);
  440. Util.Debug("<< UI.clipSend");
  441. },
  442. // Enable/disable and configure viewport clipping
  443. setViewClip: function(clip) {
  444. var display, cur_clip, pos, new_w, new_h;
  445. if (UI.rfb) {
  446. display = UI.rfb.get_display();
  447. } else {
  448. return;
  449. }
  450. cur_clip = display.get_viewport();
  451. if (typeof(clip) !== 'boolean') {
  452. // Use current setting
  453. clip = UI.getSetting('clip');
  454. }
  455. if (clip && !cur_clip) {
  456. // Turn clipping on
  457. UI.updateSetting('clip', true);
  458. } else if (!clip && cur_clip) {
  459. // Turn clipping off
  460. UI.updateSetting('clip', false);
  461. display.set_viewport(false);
  462. $D('noVNC_canvas').style.position = 'static';
  463. display.viewportChange();
  464. }
  465. if (UI.getSetting('clip')) {
  466. // If clipping, update clipping settings
  467. $D('noVNC_canvas').style.position = 'absolute';
  468. pos = Util.getPosition($D('noVNC_canvas'));
  469. new_w = window.innerWidth - pos.x;
  470. new_h = window.innerHeight - pos.y;
  471. display.set_viewport(true);
  472. display.viewportChange(0, 0, new_w, new_h);
  473. }
  474. },
  475. // Toggle/set/unset the viewport drag/move button
  476. setViewDrag: function(drag) {
  477. var vmb = $D('noVNC_view_drag_button');
  478. if (!UI.rfb) { return; }
  479. if (UI.rfb_state === 'normal' &&
  480. UI.rfb.get_display().get_viewport()) {
  481. vmb.style.display = "inline";
  482. } else {
  483. vmb.style.display = "none";
  484. }
  485. if (typeof(drag) === "undefined") {
  486. // If not specified, then toggle
  487. drag = !UI.rfb.get_viewportDrag();
  488. }
  489. if (drag) {
  490. vmb.className = "noVNC_status_button_selected";
  491. UI.rfb.set_viewportDrag(true);
  492. } else {
  493. vmb.className = "noVNC_status_button";
  494. UI.rfb.set_viewportDrag(false);
  495. }
  496. },
  497. // On touch devices, show the OS keyboard
  498. showKeyboard: function() {
  499. if(UI.keyboardVisible == false) {
  500. $D('keyboardinput').focus();
  501. UI.keyboardVisible = true;
  502. $D('showKeyboard').className = "noVNC_status_button_selected";
  503. } else if(UI.keyboardVisible == true) {
  504. $D('keyboardinput').blur();
  505. $D('showKeyboard').className = "noVNC_status_button";
  506. UI.keyboardVisible = false;
  507. }
  508. },
  509. keyInputBlur: function() {
  510. $D('showKeyboard').className = "noVNC_status_button";
  511. //Weird bug in iOS if you change keyboardVisible
  512. //here it does not actually occur so next time
  513. //you click keyboard icon it doesnt work.
  514. setTimeout("UI.setKeyboard()",100)
  515. },
  516. setKeyboard: function() {
  517. UI.keyboardVisible = false;
  518. },
  519. // iOS < Version 5 does not support position fixed. Javascript workaround:
  520. setOnscroll: function() {
  521. window.onscroll = function() {
  522. UI.setBarPosition();
  523. };
  524. },
  525. setResize: function () {
  526. window.onResize = function() {
  527. UI.setBarPosition();
  528. };
  529. },
  530. //Helper to add options to dropdown.
  531. addOption: function(selectbox,text,value )
  532. {
  533. var optn = document.createElement("OPTION");
  534. optn.text = text;
  535. optn.value = value;
  536. selectbox.options.add(optn);
  537. },
  538. setBarPosition: function() {
  539. $D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px';
  540. $D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
  541. var vncwidth = $D('noVNC_screen').style.offsetWidth;
  542. $D('noVNC-control-bar').style.width = vncwidth + 'px';
  543. }
  544. };