5 "display" : document.getElementById("display"),
6 "menu" : document.getElementById("menu"),
7 "menuControl" : document.getElementById("menuControl"),
8 "logo" : document.getElementById("status-logo"),
9 "state" : document.getElementById("state"),
13 "showClipboard": document.getElementById("showClipboard"),
14 "showKeyboard" : document.getElementById("showKeyboard"),
15 "ctrlAltDelete": document.getElementById("ctrlAltDelete"),
16 "reconnect" : document.getElementById("reconnect"),
17 "logout" : document.getElementById("logout")
22 "error" : document.getElementById("errorDialog"),
23 "clipboard": document.getElementById("clipboardDiv"),
24 "keyboard" : document.getElementById("keyboardContainer")
27 "error" : document.getElementById("errorText"),
28 "clipboard" : document.getElementById("clipboard")
32 // Constant UI initialization and behavior
35 var menu_shaded = false;
37 var shade_interval = null;
38 var show_interval = null;
40 // Cache error image (might not be available when error occurs)
41 var guacErrorImage = new Image();
42 guacErrorImage.src = "images/noguacamole-logo-24.png";
44 GuacamoleUI.showError = function(error) {
46 GuacamoleUI.menu.className = "error";
47 GuacamoleUI.display.className += " guac-error";
49 GuacamoleUI.logo.src = guacErrorImage.src;
50 GuacamoleUI.error.textContent = error;
51 GuacamoleUI.containers.error.style.visibility = "visible";
55 GuacamoleUI.shadeMenu = function() {
59 var step = Math.floor(GuacamoleUI.menu.offsetHeight / 10) + 1;
63 window.clearInterval(show_interval);
64 shade_interval = window.setInterval(function() {
67 GuacamoleUI.menu.style.top = offset + "px";
69 if (offset <= -GuacamoleUI.menu.offsetHeight) {
70 window.clearInterval(shade_interval);
71 GuacamoleUI.menu.style.visiblity = "hidden";
79 GuacamoleUI.showMenu = function() {
83 var step = Math.floor(GuacamoleUI.menu.offsetHeight / 5) + 1;
84 var offset = -GuacamoleUI.menu.offsetHeight;
86 GuacamoleUI.menu.style.visiblity = "";
88 window.clearInterval(shade_interval);
89 show_interval = window.setInterval(function() {
95 window.clearInterval(show_interval);
98 GuacamoleUI.menu.style.top = offset + "px";
105 // Show/Hide clipboard
106 GuacamoleUI.buttons.showClipboard.onclick = function() {
108 var displayed = GuacamoleUI.containers.clipboard.style.display;
109 if (displayed != "block") {
110 GuacamoleUI.containers.clipboard.style.display = "block";
111 GuacamoleUI.buttons.showClipboard.innerHTML = "Hide Clipboard";
114 GuacamoleUI.containers.clipboard.style.display = "none";
115 GuacamoleUI.buttons.showClipboard.innerHTML = "Show Clipboard";
116 GuacamoleUI.clipboard.onchange();
121 // Show/Hide keyboard
122 var keyboardResizeInterval = null;
123 GuacamoleUI.buttons.showKeyboard.onclick = function() {
125 var displayed = GuacamoleUI.containers.keyboard.style.display;
126 if (displayed != "block") {
127 GuacamoleUI.containers.keyboard.style.display = "block";
128 GuacamoleUI.buttons.showKeyboard.textContent = "Hide Keyboard";
130 // Automatically update size
131 window.onresize = updateKeyboardSize;
132 keyboardResizeInterval = window.setInterval(updateKeyboardSize, 30);
134 updateKeyboardSize();
137 GuacamoleUI.containers.keyboard.style.display = "none";
138 GuacamoleUI.buttons.showKeyboard.textContent = "Show Keyboard";
140 window.onresize = null;
141 window.clearInterval(keyboardResizeInterval);
147 GuacamoleUI.buttons.logout.onclick = function() {
148 window.location.href = "logout";
151 // Timeouts for detecting if users wants menu to open or close
152 var detectMenuOpenTimeout = null;
153 var detectMenuCloseTimeout = null;
155 // Clear detection timeouts
156 function resetMenuDetect() {
158 if (detectMenuOpenTimeout != null) {
159 window.clearTimeout(detectMenuOpenTimeout);
160 detectMenuOpenTimeout = null;
163 if (detectMenuCloseTimeout != null) {
164 window.clearTimeout(detectMenuCloseTimeout);
165 detectMenuCloseTimeout = null;
170 // Initiate detection of menu open action. If not canceled through some
171 // user event, menu will open.
172 function startMenuOpenDetect() {
174 // Clear detection state
177 // Wait and then show menu
178 detectMenuOpenTimeout = window.setTimeout(function() {
179 GuacamoleUI.showMenu();
180 detectMenuOpenTimeout = null;
185 // Initiate detection of menu close action. If not canceled through some
186 // user event, menu will close.
187 function startMenuCloseDetect() {
189 // Clear detection state
192 // Wait and then shade menu
193 detectMenuCloseTimeout = window.setTimeout(function() {
194 GuacamoleUI.shadeMenu();
195 detectMenuCloseTimeout = null;
200 // Show menu if mouseover any part of menu
201 GuacamoleUI.menu.addEventListener('mouseover', GuacamoleUI.showMenu, true);
203 // Stop detecting menu state change intents if mouse is over menu
204 GuacamoleUI.menu.addEventListener('mouseover', resetMenuDetect, true);
206 // When mouse hovers over top of screen, start detection of intent to open menu
207 GuacamoleUI.menuControl.addEventListener('mousemove', startMenuOpenDetect, true);
209 // When mouse enters display, start detection of intent to close menu
210 GuacamoleUI.display.addEventListener('mouseover', startMenuCloseDetect, true);
212 // Show menu if mouse leaves document
213 document.addEventListener('mouseout', function(e) {
215 // Get parent of the element the mouse pointer is leaving
216 if (!e) e = window.event;
217 var target = e.relatedTarget || e.toElement;
219 // Ensure target is not document nor child of document
220 var targetParent = target;
221 while (targetParent != null) {
222 if (targetParent == document) return;
223 targetParent = targetParent.parentNode;
226 // Start detection of intent to open menu
227 startMenuOpenDetect();
232 GuacamoleUI.buttons.reconnect.onclick = function() {
233 window.location.reload();
236 // On-screen keyboard
237 GuacamoleUI.keyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty.xml");
238 GuacamoleUI.containers.keyboard.appendChild(GuacamoleUI.keyboard.getElement());
240 // Function for automatically updating keyboard size
241 var lastKeyboardWidth;
242 function updateKeyboardSize() {
243 var currentSize = GuacamoleUI.keyboard.getElement().offsetWidth;
244 if (lastKeyboardWidth != currentSize) {
245 GuacamoleUI.keyboard.resize(currentSize);
246 lastKeyboardWidth = currentSize;
252 // Tie UI events / behavior to a specific Guacamole client
253 GuacamoleUI.attach = function(guac) {
256 var mouse = new Guacamole.Mouse(GuacamoleUI.display);
257 mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
258 function(mouseState) {
259 guac.sendMouseState(mouseState);
263 var keyboard = new Guacamole.Keyboard(document);
265 function disableKeyboard() {
266 keyboard.onkeydown = null;
267 keyboard.onkeyup = null;
270 function enableKeyboard() {
273 guac.sendKeyEvent(1, keysym);
278 guac.sendKeyEvent(0, keysym);
282 // Enable keyboard by default
285 // Handle client state change
286 guac.onstatechange = function(clientState) {
287 switch (clientState) {
291 GuacamoleUI.state.textContent = "Idle."
296 GuacamoleUI.state.textContent = "Connecting...";
299 // Connected + waiting
301 GuacamoleUI.state.textContent = "Connected, waiting for first update...";
307 GuacamoleUI.display.className =
308 GuacamoleUI.display.className.replace(/guac-loading/, '');
310 GuacamoleUI.menu.className = "connected";
311 GuacamoleUI.state.textContent = "Connected.";
312 GuacamoleUI.shadeMenu();
317 GuacamoleUI.state.textContent = "Disconnecting...";
322 GuacamoleUI.state.textContent = "Disconnected.";
325 // Unknown status code
327 GuacamoleUI.state.textContent = "Unknown";
332 // Name instruction handler
333 guac.onname = function(name) {
334 document.title = name;
338 guac.onerror = function(error) {
340 // Disconnect, if connected
343 // Display error message
344 GuacamoleUI.showError(error);
346 // Show error by desaturating display
347 var layers = guac.getLayers();
348 for (var i=0; i<layers.length; i++) {
349 layers[i].filter(desaturateFilter);
352 // Filter for desaturation
353 function desaturateFilter(data, width, height) {
355 for (var i=0; i<data.length; i+=4) {
363 var v = Math.max(r, g, b) / 2;
374 // Disconnect on close
375 window.onunload = function() {
379 // Handle clipboard events
380 GuacamoleUI.clipboard.onchange = function() {
382 var text = GuacamoleUI.clipboard.value;
383 guac.setClipboard(text);
387 // Ignore keypresses when clipboard is focused
388 GuacamoleUI.clipboard.onfocus = function() {
392 // Capture keypresses when clipboard is not focused
393 GuacamoleUI.clipboard.onblur = function() {
397 // Server copy handler
398 guac.onclipboard = function(data) {
399 GuacamoleUI.clipboard.value = data;
402 GuacamoleUI.keyboard.onkeypress = function(keysym) {
403 guac.sendKeyEvent(1, keysym);
406 GuacamoleUI.keyboard.onkeyrelease = function(keysym) {
407 guac.sendKeyEvent(0, keysym);
410 // Send Ctrl-Alt-Delete
411 GuacamoleUI.buttons.ctrlAltDelete.onclick = function() {
413 var KEYSYM_CTRL = 0xFFE3;
414 var KEYSYM_ALT = 0xFFE9;
415 var KEYSYM_DELETE = 0xFFFF;
417 guac.sendKeyEvent(1, KEYSYM_CTRL);
418 guac.sendKeyEvent(1, KEYSYM_ALT);
419 guac.sendKeyEvent(1, KEYSYM_DELETE);
420 guac.sendKeyEvent(0, KEYSYM_DELETE);
421 guac.sendKeyEvent(0, KEYSYM_ALT);
422 guac.sendKeyEvent(0, KEYSYM_CTRL);