5 "viewport" : document.getElementById("viewportClone"),
6 "display" : document.getElementById("display"),
7 "menu" : document.getElementById("menu"),
8 "menuControl" : document.getElementById("menuControl"),
9 "touchMenu" : document.getElementById("touchMenu"),
10 "logo" : document.getElementById("status-logo"),
14 "showClipboard": document.getElementById("showClipboard"),
15 "showKeyboard" : document.getElementById("showKeyboard"),
16 "ctrlAltDelete": document.getElementById("ctrlAltDelete"),
17 "reconnect" : document.getElementById("reconnect"),
18 "logout" : document.getElementById("logout")
23 "state" : document.getElementById("statusDialog"),
24 "clipboard": document.getElementById("clipboardDiv"),
25 "keyboard" : document.getElementById("keyboardContainer")
28 "state" : document.getElementById("statusText"),
29 "clipboard" : document.getElementById("clipboard")
33 // Constant UI initialization and behavior
36 var menu_shaded = false;
38 var shade_interval = null;
39 var show_interval = null;
41 // Cache error image (might not be available when error occurs)
42 var guacErrorImage = new Image();
43 guacErrorImage.src = "images/noguacamole-logo-24.png";
45 GuacamoleUI.hideStatus = function() {
46 document.body.classList.remove("guac-error");
47 GuacamoleUI.containers.state.style.visibility = "hidden";
50 GuacamoleUI.showStatus = function(text) {
51 document.body.classList.remove("guac-error");
52 GuacamoleUI.containers.state.style.visibility = "visible";
53 GuacamoleUI.state.textContent = text;
56 GuacamoleUI.showError = function(error) {
57 document.body.classList.add("guac-error");
58 GuacamoleUI.state.textContent = error;
61 GuacamoleUI.shadeMenu = function() {
65 var step = Math.floor(GuacamoleUI.menu.offsetHeight / 10) + 1;
69 window.clearInterval(show_interval);
70 shade_interval = window.setInterval(function() {
73 GuacamoleUI.menu.style.top = offset + "px";
75 if (offset <= -GuacamoleUI.menu.offsetHeight) {
76 window.clearInterval(shade_interval);
77 GuacamoleUI.menu.style.visiblity = "hidden";
85 GuacamoleUI.showMenu = function() {
89 var step = Math.floor(GuacamoleUI.menu.offsetHeight / 5) + 1;
90 var offset = -GuacamoleUI.menu.offsetHeight;
92 GuacamoleUI.menu.style.visiblity = "";
94 window.clearInterval(shade_interval);
95 show_interval = window.setInterval(function() {
101 window.clearInterval(show_interval);
104 GuacamoleUI.menu.style.top = offset + "px";
111 // Show/Hide clipboard
112 GuacamoleUI.buttons.showClipboard.onclick = function() {
114 var displayed = GuacamoleUI.containers.clipboard.style.display;
115 if (displayed != "block") {
116 GuacamoleUI.containers.clipboard.style.display = "block";
117 GuacamoleUI.buttons.showClipboard.innerHTML = "Hide Clipboard";
120 GuacamoleUI.containers.clipboard.style.display = "none";
121 GuacamoleUI.buttons.showClipboard.innerHTML = "Show Clipboard";
122 GuacamoleUI.clipboard.onchange();
127 // Show/Hide keyboard
128 var keyboardResizeInterval = null;
129 GuacamoleUI.buttons.showKeyboard.onclick = function() {
131 var displayed = GuacamoleUI.containers.keyboard.style.display;
132 if (displayed != "block") {
133 GuacamoleUI.containers.keyboard.style.display = "block";
134 GuacamoleUI.buttons.showKeyboard.textContent = "Hide Keyboard";
136 // Automatically update size
137 window.onresize = updateKeyboardSize;
138 keyboardResizeInterval = window.setInterval(updateKeyboardSize, 30);
140 updateKeyboardSize();
143 GuacamoleUI.containers.keyboard.style.display = "none";
144 GuacamoleUI.buttons.showKeyboard.textContent = "Show Keyboard";
146 window.onresize = null;
147 window.clearInterval(keyboardResizeInterval);
153 GuacamoleUI.buttons.logout.onclick = function() {
154 window.location.href = "logout";
157 // Timeouts for detecting if users wants menu to open or close
158 var detectMenuOpenTimeout = null;
159 var detectMenuCloseTimeout = null;
161 // Clear detection timeouts
162 GuacamoleUI.resetMenuDetect = function() {
164 if (detectMenuOpenTimeout != null) {
165 window.clearTimeout(detectMenuOpenTimeout);
166 detectMenuOpenTimeout = null;
169 if (detectMenuCloseTimeout != null) {
170 window.clearTimeout(detectMenuCloseTimeout);
171 detectMenuCloseTimeout = null;
176 // Initiate detection of menu open action. If not canceled through some
177 // user event, menu will open.
178 GuacamoleUI.startMenuOpenDetect = function() {
180 if (!detectMenuOpenTimeout) {
182 // Clear detection state
183 GuacamoleUI.resetMenuDetect();
185 // Wait and then show menu
186 detectMenuOpenTimeout = window.setTimeout(function() {
187 GuacamoleUI.showMenu();
188 detectMenuOpenTimeout = null;
195 // Initiate detection of menu close action. If not canceled through some
196 // user event, menu will close.
197 GuacamoleUI.startMenuCloseDetect = function() {
199 if (!detectMenuCloseTimeout) {
201 // Clear detection state
202 GuacamoleUI.resetMenuDetect();
204 // Wait and then shade menu
205 detectMenuCloseTimeout = window.setTimeout(function() {
206 GuacamoleUI.shadeMenu();
207 detectMenuCloseTimeout = null;
214 // Show menu if mouseover any part of menu
215 GuacamoleUI.menu.addEventListener('mouseover', GuacamoleUI.showMenu, true);
217 // Stop detecting menu state change intents if mouse is over menu
218 GuacamoleUI.menu.addEventListener('mouseover', GuacamoleUI.resetMenuDetect, true);
220 // When mouse hovers over top of screen, start detection of intent to open menu
221 GuacamoleUI.menuControl.addEventListener('mousemove', GuacamoleUI.startMenuOpenDetect, true);
223 // When mouse enters display, start detection of intent to close menu
224 GuacamoleUI.display.addEventListener('mouseover', GuacamoleUI.startMenuCloseDetect, true);
226 var menuShowLongPressTimeout = null;
228 GuacamoleUI.startLongPressDetect = function() {
230 if (!menuShowLongPressTimeout) {
232 menuShowLongPressTimeout = window.setTimeout(function() {
234 menuShowLongPressTimeout = null;
235 GuacamoleUI.showMenu();
242 GuacamoleUI.stopLongPressDetect = function() {
243 window.clearTimeout(menuShowLongPressTimeout);
244 menuShowLongPressTimeout = null;
247 // Detect long-press at bottom of screen
248 document.body.addEventListener('touchstart', GuacamoleUI.startLongPressDetect, true);
250 // Show menu if mouse leaves document
251 document.addEventListener('mouseout', function(e) {
253 // Get parent of the element the mouse pointer is leaving
254 if (!e) e = window.event;
255 var target = e.relatedTarget || e.toElement;
257 // Ensure target is not document nor child of document
258 var targetParent = target;
259 while (targetParent != null) {
260 if (targetParent == document) return;
261 targetParent = targetParent.parentNode;
264 // Start detection of intent to open menu
265 GuacamoleUI.startMenuOpenDetect();
270 GuacamoleUI.buttons.reconnect.onclick = function() {
271 window.location.reload();
274 GuacamoleUI.display.onclick = function(e) {
279 // On-screen keyboard
280 GuacamoleUI.keyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty-mobile.xml");
281 GuacamoleUI.containers.keyboard.appendChild(GuacamoleUI.keyboard.getElement());
283 // Function for automatically updating keyboard size
284 var lastKeyboardWidth;
285 function updateKeyboardSize() {
286 var currentSize = GuacamoleUI.keyboard.getElement().offsetWidth;
287 if (lastKeyboardWidth != currentSize) {
288 GuacamoleUI.keyboard.resize(currentSize);
289 lastKeyboardWidth = currentSize;
295 // Tie UI events / behavior to a specific Guacamole client
296 GuacamoleUI.attach = function(guac) {
299 var mouse = new Guacamole.Mouse(GuacamoleUI.display);
300 mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
301 function(mouseState) {
303 // Determine mouse position within view
304 var mouse_view_x = mouseState.x + GuacamoleUI.display.offsetLeft - window.pageXOffset;
305 var mouse_view_y = mouseState.y + GuacamoleUI.display.offsetTop - window.pageYOffset;
307 // Determine viewport dimensioins
308 var view_width = GuacamoleUI.viewport.offsetWidth;
309 var view_height = GuacamoleUI.viewport.offsetHeight;
311 // Determine scroll amounts based on mouse position relative to document
314 if (mouse_view_x > view_width)
315 scroll_amount_x = mouse_view_x - view_width;
316 else if (mouse_view_x < 0)
317 scroll_amount_x = mouse_view_x;
322 if (mouse_view_y > view_height)
323 scroll_amount_y = mouse_view_y - view_height;
324 else if (mouse_view_y < 0)
325 scroll_amount_y = mouse_view_y;
329 // Scroll (if necessary) to keep mouse on screen.
330 window.scrollBy(scroll_amount_x, scroll_amount_y);
332 // Hide menu on movement
333 GuacamoleUI.startMenuCloseDetect();
335 // Stop detecting long presses if mouse is being used
336 GuacamoleUI.stopLongPressDetect();
339 guac.sendMouseState(mouseState);
344 var keyboard = new Guacamole.Keyboard(document);
346 function disableKeyboard() {
347 keyboard.onkeydown = null;
348 keyboard.onkeyup = null;
351 function enableKeyboard() {
354 guac.sendKeyEvent(1, keysym);
359 guac.sendKeyEvent(0, keysym);
363 // Enable keyboard by default
366 // Handle client state change
367 guac.onstatechange = function(clientState) {
368 switch (clientState) {
372 GuacamoleUI.showStatus("Idle.");
377 GuacamoleUI.shadeMenu();
378 GuacamoleUI.showStatus("Connecting...");
381 // Connected + waiting
383 GuacamoleUI.showStatus("Connected, waiting for first update...");
389 GuacamoleUI.hideStatus();
390 GuacamoleUI.display.className =
391 GuacamoleUI.display.className.replace(/guac-loading/, '');
393 GuacamoleUI.menu.className = "connected";
398 GuacamoleUI.showStatus("Disconnecting...");
403 GuacamoleUI.showStatus("Disconnected.");
406 // Unknown status code
408 GuacamoleUI.showStatus("[UNKNOWN STATUS]");
413 // Name instruction handler
414 guac.onname = function(name) {
415 document.title = name;
419 guac.onerror = function(error) {
421 // Disconnect, if connected
424 // Display error message
425 GuacamoleUI.showError(error);
427 // Show error by desaturating display
428 var layers = guac.getLayers();
429 for (var i=0; i<layers.length; i++) {
430 layers[i].filter(desaturateFilter);
433 // Filter for desaturation
434 function desaturateFilter(data, width, height) {
436 for (var i=0; i<data.length; i+=4) {
444 var v = Math.max(r, g, b) / 2;
455 // Disconnect on close
456 window.onunload = function() {
460 // Handle clipboard events
461 GuacamoleUI.clipboard.onchange = function() {
463 var text = GuacamoleUI.clipboard.value;
464 guac.setClipboard(text);
468 // Ignore keypresses when clipboard is focused
469 GuacamoleUI.clipboard.onfocus = function() {
473 // Capture keypresses when clipboard is not focused
474 GuacamoleUI.clipboard.onblur = function() {
478 // Server copy handler
479 guac.onclipboard = function(data) {
480 GuacamoleUI.clipboard.value = data;
483 GuacamoleUI.keyboard.onkeydown = function(keysym) {
484 guac.sendKeyEvent(1, keysym);
487 GuacamoleUI.keyboard.onkeyup = function(keysym) {
488 guac.sendKeyEvent(0, keysym);
491 // Send Ctrl-Alt-Delete
492 GuacamoleUI.buttons.ctrlAltDelete.onclick = function() {
494 var KEYSYM_CTRL = 0xFFE3;
495 var KEYSYM_ALT = 0xFFE9;
496 var KEYSYM_DELETE = 0xFFFF;
498 guac.sendKeyEvent(1, KEYSYM_CTRL);
499 guac.sendKeyEvent(1, KEYSYM_ALT);
500 guac.sendKeyEvent(1, KEYSYM_DELETE);
501 guac.sendKeyEvent(0, KEYSYM_DELETE);
502 guac.sendKeyEvent(0, KEYSYM_ALT);
503 guac.sendKeyEvent(0, KEYSYM_CTRL);