* See README.md for usage and integration instructions.
*/
-/* jslint white: false, browser: true */
-/* global window, document.getElementById, Util, WebUtil, RFB, Display */
-
import * as Log from '../core/util/logging.js';
import _, { l10n } from './localization.js';
-import { isTouchDevice } from '../core/util/browsers.js';
+import { isTouchDevice } from '../core/util/browser.js';
import { setCapture, getPointerEvent } from '../core/util/events.js';
import KeyTable from "../core/input/keysym.js";
import keysyms from "../core/input/keysymdef.js";
import Keyboard from "../core/input/keyboard.js";
import RFB from "../core/rfb.js";
-import Display from "../core/display.js";
import * as WebUtil from "./webutil.js";
var UI = {
connected: false,
desktopName: "",
- resizeTimeout: null,
statusTimeout: null,
hideKeyboardTimeout: null,
idleControlbarTimeout: null,
UI.initFullscreen();
// Setup event handlers
- UI.addResizeHandlers();
UI.addControlbarHandlers();
UI.addTouchSpecificHandlers();
UI.addExtraKeysHandlers();
UI.openControlbar();
- UI.updateViewClip();
-
UI.updateVisualState('init');
document.documentElement.classList.remove("noVNC_loading");
* EVENT HANDLERS
* ------v------*/
- addResizeHandlers: function() {
- window.addEventListener('resize', UI.applyResizeMode);
- window.addEventListener('resize', UI.updateViewClip);
- },
-
addControlbarHandlers: function() {
document.getElementById("noVNC_control_bar")
.addEventListener('mousemove', UI.activateControlbar);
UI.disableSetting('port');
UI.disableSetting('path');
UI.disableSetting('repeaterID');
- UI.updateViewClip();
UI.setMouseButton(1);
// Hide the controlbar after 2 seconds
UI.keepControlbar();
}
- // Hide input related buttons in view only mode
- if (UI.rfb && UI.rfb.viewOnly) {
- document.getElementById('noVNC_keyboard_button')
- .classList.add('noVNC_hidden');
- document.getElementById('noVNC_toggle_extra_keys_button')
- .classList.add('noVNC_hidden');
- } else {
- document.getElementById('noVNC_keyboard_button')
- .classList.remove('noVNC_hidden');
- document.getElementById('noVNC_toggle_extra_keys_button')
- .classList.remove('noVNC_hidden');
- }
-
// State change disables viewport dragging.
// It is enabled (toggled) by direct click on the button
UI.setViewDrag(false);
},
toggleControlbarSide: function () {
- // Temporarily disable animation to avoid weird movement
+ // Temporarily disable animation, if bar is displayed, to avoid weird
+ // movement. The transitionend-event will not fire when display=none.
var bar = document.getElementById('noVNC_control_bar');
- bar.style.transitionDuration = '0s';
- bar.addEventListener('transitionend', function () { this.style.transitionDuration = ""; });
+ var barDisplayStyle = window.getComputedStyle(bar).display;
+ if (barDisplayStyle !== 'none') {
+ bar.style.transitionDuration = '0s';
+ bar.addEventListener('transitionend', function () {
+ this.style.transitionDuration = ""; });
+ }
var anchor = document.getElementById('noVNC_control_bar_anchor');
if (anchor.classList.contains("noVNC_right")) {
if (val === null) {
val = WebUtil.readSetting(name, defVal);
}
- UI.updateSetting(name, val);
+ WebUtil.setSetting(name, val);
+ UI.updateSetting(name);
return val;
},
// Update cookie and form control setting. If value is not set, then
// updates from control to current cookie setting.
- updateSetting: function(name, value) {
-
- // Save the cookie for this session
- if (typeof value !== 'undefined') {
- WebUtil.writeSetting(name, value);
- }
+ updateSetting: function(name) {
// Update the settings control
- value = UI.getSetting(name);
+ var value = UI.getSetting(name);
var ctrl = document.getElementById('noVNC_setting_' + name);
if (ctrl.type === 'checkbox') {
},
connect: function(event, password) {
+
+ // Ignore when rfb already exists
+ if (typeof UI.rfb !== 'undefined') {
+ return;
+ }
+
var host = UI.getSetting('host');
var port = UI.getSetting('port');
var path = UI.getSetting('path');
UI.updateVisualState('connecting');
- UI.updateViewOnly();
-
var url;
url = UI.getSetting('encrypt') ? 'wss' : 'ws';
}
url += '/' + path;
- UI.rfb = new RFB(document.getElementById('noVNC_canvas'), url,
+ UI.rfb = new RFB(document.getElementById('noVNC_container'), url,
{ shared: UI.getSetting('shared'),
repeaterID: UI.getSetting('repeaterID'),
credentials: { password: password } });
UI.rfb.addEventListener("disconnect", UI.disconnectFinished);
UI.rfb.addEventListener("credentialsrequired", UI.credentials);
UI.rfb.addEventListener("securityfailure", UI.securityFailed);
- UI.rfb.addEventListener("capabilities", function () { UI.updatePowerButton(); UI.initialResize(); });
+ UI.rfb.addEventListener("capabilities", function () { UI.updatePowerButton(); });
UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
UI.rfb.addEventListener("bell", UI.bell);
- UI.rfb.addEventListener("fbresize", UI.updateSessionSize);
UI.rfb.addEventListener("desktopname", UI.updateDesktopName);
+ UI.rfb.clipViewport = UI.getSetting('view_clip');
+ UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
+ UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
+
+ UI.updateViewOnly(); // requires UI.rfb
},
disconnect: function() {
connectFinished: function (e) {
UI.connected = true;
UI.inhibit_reconnect = false;
- UI.doneInitialResize = false;
let msg;
if (UI.getSetting('encrypt')) {
UI.updateVisualState('connected');
// Do this last because it can only be used on rendered elements
- document.getElementById('noVNC_canvas').focus();
+ UI.rfb.focus();
},
disconnectFinished: function (e) {
+ let wasConnected = UI.connected;
+
// This variable is ideally set when disconnection starts, but
// when the disconnection isn't clean or if it is initiated by
// the server, we need to do it here as well since
// UI.disconnect() won't be used in those cases.
UI.connected = false;
+ UI.rfb = undefined;
+
if (!e.detail.clean) {
UI.updateVisualState('disconnected');
- UI.showStatus(_("Something went wrong, connection is closed"),
- 'error');
+ if (wasConnected) {
+ UI.showStatus(_("Something went wrong, connection is closed"),
+ 'error');
+ } else {
+ UI.showStatus(_("Failed to connect to server"), 'error');
+ }
} else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) {
UI.updateVisualState('reconnecting');
applyResizeMode: function() {
if (!UI.rfb) return;
- var screen = UI.screenSize();
-
- if (screen && UI.connected) {
-
- var resizeMode = UI.getSetting('resize');
- UI.rfb.viewportScale = 1.0;
-
- // Make sure the viewport is adjusted first
- UI.updateViewClip();
-
- if (resizeMode === 'remote') {
-
- // Request changing the resolution of the remote display to
- // the size of the local browser viewport.
-
- // In order to not send multiple requests before the browser-resize
- // is finished we wait 0.5 seconds before sending the request.
- clearTimeout(UI.resizeTimeout);
- UI.resizeTimeout = setTimeout(function(){
- // Request a remote size covering the viewport
- if (UI.rfb.requestDesktopSize(screen.w, screen.h)) {
- Log.Debug('Requested new desktop size: ' +
- screen.w + 'x' + screen.h);
- }
- }, 500);
-
- } else {
- UI.updateScaling();
- }
- }
- },
-
- // Re-calculate local scaling
- updateScaling: function() {
- if (!UI.rfb) return;
-
- var resizeMode = UI.getSetting('resize');
- if (resizeMode !== 'scale') {
- return;
- }
-
- var screen = UI.screenSize();
-
- if (!screen || !UI.connected) {
- return;
- }
-
- UI.rfb.autoscale(screen.w, screen.h);
- UI.fixScrollbars();
- },
-
- // Gets the the size of the available viewport in the browser window
- screenSize: function() {
- var screen = document.getElementById('noVNC_screen');
- return {w: screen.offsetWidth, h: screen.offsetHeight};
- },
-
- // Normally we only apply the current resize mode after a window resize
- // event. This means that when a new connection is opened, there is no
- // resize mode active.
- // We have to wait until we know the capabilities of the server as
- // some calls later in the chain is dependant on knowing the
- // server-capabilities.
- initialResize: function() {
- if (UI.doneInitialResize) return;
-
- UI.applyResizeMode();
- UI.doneInitialResize = true;
+ UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
+ UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
},
/* ------^-------
* VIEW CLIPPING
* ------v------*/
- // Set and configure viewport clipping
- setViewClip: function(clip) {
- UI.updateSetting('view_clip', clip);
- UI.updateViewClip();
- },
-
// Update parameters that depend on the viewport clip setting
updateViewClip: function() {
if (!UI.rfb) return;
var cur_clip = UI.rfb.clipViewport;
var new_clip = UI.getSetting('view_clip');
- var resizeSetting = UI.getSetting('resize');
- if (resizeSetting === 'scale') {
- // Disable viewport clipping if we are scaling
- new_clip = false;
- } else if (isTouchDevice) {
+ if (isTouchDevice) {
// Touch devices usually have shit scrollbars
new_clip = true;
}
UI.rfb.clipViewport = new_clip;
}
- var size = UI.screenSize();
-
- if (new_clip && size) {
- // When clipping is enabled, the screen is limited to
- // the size of the browser window.
- UI.rfb.viewportChangeSize(size.w, size.h);
- UI.fixScrollbars();
- }
-
// Changing the viewport may change the state of
// the dragging button
UI.updateViewDrag();
},
updateViewDrag: function() {
- var clipping = false;
-
if (!UI.connected) return;
- // Check if viewport drag is possible. It is only possible
- // if the remote display is clipping the client display.
- if (UI.rfb.clipViewport && UI.rfb.isClipped) {
- clipping = true;
- }
-
var viewDragButton = document.getElementById('noVNC_view_drag_button');
- if (!clipping &&
- UI.rfb.dragViewport) {
- // The size of the remote display is the same or smaller
- // than the client display. Make sure viewport drag isn't
- // active when it can't be used.
+ if (!UI.rfb.clipViewport && UI.rfb.dragViewport) {
+ // We are no longer clipping the viewport. Make sure
+ // viewport drag isn't active when it can't be used.
UI.rfb.dragViewport = false;
}
if (isTouchDevice) {
viewDragButton.classList.remove("noVNC_hidden");
- if (clipping) {
+ if (UI.rfb.clipViewport) {
viewDragButton.disabled = false;
} else {
viewDragButton.disabled = true;
} else {
viewDragButton.disabled = false;
- if (clipping) {
+ if (UI.rfb.clipViewport) {
viewDragButton.classList.remove("noVNC_hidden");
} else {
viewDragButton.classList.add("noVNC_hidden");
var l = input.value.length;
// Move the caret to the end
input.setSelectionRange(l, l);
- } catch (err) {} // setSelectionRange is undefined in Google Chrome
+ } catch (err) {
+ // setSelectionRange is undefined in Google Chrome
+ }
},
hideVirtualKeyboard: function() {
document.getElementById('noVNC_keyboard_button')
.classList.add("noVNC_selected");
if (UI.rfb) {
- UI.rfb.set_focus_on_click(false);
+ UI.rfb.focusOnClick = false;
}
},
document.getElementById('noVNC_keyboard_button')
.classList.remove("noVNC_selected");
if (UI.rfb) {
- UI.rfb.set_focus_on_click(true);
+ UI.rfb.focusOnClick = true;
}
},
updateViewOnly: function() {
if (!UI.rfb) return;
UI.rfb.viewOnly = UI.getSetting('view_only');
+
+ // Hide input related buttons in view only mode
+ if (UI.rfb.viewOnly) {
+ document.getElementById('noVNC_keyboard_button')
+ .classList.add('noVNC_hidden');
+ document.getElementById('noVNC_toggle_extra_keys_button')
+ .classList.add('noVNC_hidden');
+ } else {
+ document.getElementById('noVNC_keyboard_button')
+ .classList.remove('noVNC_hidden');
+ document.getElementById('noVNC_toggle_extra_keys_button')
+ .classList.remove('noVNC_hidden');
+ }
+ UI.setMouseButton(1); //has it's own logic for hiding/showing
},
updateLogging: function() {
WebUtil.init_logging(UI.getSetting('logging'));
},
- updateSessionSize: function(e) {
- UI.updateViewClip();
- UI.updateScaling();
- UI.fixScrollbars();
- },
-
- fixScrollbars: function() {
- // This is a hack because Chrome screws up the calculation
- // for when scrollbars are needed. So to fix it we temporarily
- // toggle them off and on.
- var screen = document.getElementById('noVNC_screen');
- screen.style.overflow = 'hidden';
- // Force Chrome to recalculate the layout by asking for
- // an element's dimensions
- screen.getBoundingClientRect();
- screen.style.overflow = "";
- },
-
updateDesktopName: function(e) {
UI.desktopName = e.detail.name;
// Display the desktop name in the document title
};
// Set up translations
-var LINGUAS = ["de", "el", "nl", "pl", "sv"];
+var LINGUAS = ["de", "el", "es", "nl", "pl", "sv", "tr", "zh_CN", "zh_TW"];
l10n.setup(LINGUAS);
if (l10n.language !== "en" && l10n.dictionary === undefined) {
WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', function (translations) {
// wait for translations to load before loading the UI
UI.prime();
}, function (err) {
- throw err;
+ Log.Error("Failed to load translations: " + err);
+ UI.prime();
});
} else {
UI.prime();