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 GuacamoleUI.display.onclick = function(e) {
241 // On-screen keyboard
242 GuacamoleUI.keyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty-mobile.xml");
243 GuacamoleUI.containers.keyboard.appendChild(GuacamoleUI.keyboard.getElement());
245 // Function for automatically updating keyboard size
246 var lastKeyboardWidth;
247 function updateKeyboardSize() {
248 var currentSize = GuacamoleUI.keyboard.getElement().offsetWidth;
249 if (lastKeyboardWidth != currentSize) {
250 GuacamoleUI.keyboard.resize(currentSize);
251 lastKeyboardWidth = currentSize;
257 // Tie UI events / behavior to a specific Guacamole client
258 GuacamoleUI.attach = function(guac) {
261 var mouse = new Guacamole.Mouse(GuacamoleUI.display);
262 mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
263 function(mouseState) {
265 // Determine mouse position within view
266 var mouse_view_x = mouseState.x + GuacamoleUI.display.offsetLeft - window.pageXOffset;
267 var mouse_view_y = mouseState.y + GuacamoleUI.display.offsetTop - window.pageYOffset;
269 var view_width = document.body.clientWidth;
270 var view_height = document.body.clientHeight;
272 // Determine scroll amounts based on mouse position relative to document
275 if (mouse_view_x > view_width)
276 scroll_amount_x = mouse_view_x - view_width;
277 else if (mouse_view_x < 0)
278 scroll_amount_x = mouse_view_x;
283 if (mouse_view_y > view_height)
284 scroll_amount_y = mouse_view_y - view_height;
285 else if (mouse_view_y < 0)
286 scroll_amount_y = mouse_view_y;
290 // Scroll (if necessary) to keep mouse on screen.
291 window.scrollBy(scroll_amount_x, scroll_amount_y);
294 guac.sendMouseState(mouseState);
299 var keyboard = new Guacamole.Keyboard(document);
301 function disableKeyboard() {
302 keyboard.onkeydown = null;
303 keyboard.onkeyup = null;
306 function enableKeyboard() {
309 guac.sendKeyEvent(1, keysym);
314 guac.sendKeyEvent(0, keysym);
318 // Enable keyboard by default
321 // Handle client state change
322 guac.onstatechange = function(clientState) {
323 switch (clientState) {
327 GuacamoleUI.state.textContent = "Idle."
332 GuacamoleUI.state.textContent = "Connecting...";
335 // Connected + waiting
337 GuacamoleUI.state.textContent = "Connected, waiting for first update...";
343 GuacamoleUI.display.className =
344 GuacamoleUI.display.className.replace(/guac-loading/, '');
346 GuacamoleUI.menu.className = "connected";
347 GuacamoleUI.state.textContent = "Connected.";
348 GuacamoleUI.shadeMenu();
353 GuacamoleUI.state.textContent = "Disconnecting...";
358 GuacamoleUI.state.textContent = "Disconnected.";
361 // Unknown status code
363 GuacamoleUI.state.textContent = "Unknown";
368 // Name instruction handler
369 guac.onname = function(name) {
370 document.title = name;
374 guac.onerror = function(error) {
376 // Disconnect, if connected
379 // Display error message
380 GuacamoleUI.showError(error);
382 // Show error by desaturating display
383 var layers = guac.getLayers();
384 for (var i=0; i<layers.length; i++) {
385 layers[i].filter(desaturateFilter);
388 // Filter for desaturation
389 function desaturateFilter(data, width, height) {
391 for (var i=0; i<data.length; i+=4) {
399 var v = Math.max(r, g, b) / 2;
410 // Disconnect on close
411 window.onunload = function() {
415 // Handle clipboard events
416 GuacamoleUI.clipboard.onchange = function() {
418 var text = GuacamoleUI.clipboard.value;
419 guac.setClipboard(text);
423 // Ignore keypresses when clipboard is focused
424 GuacamoleUI.clipboard.onfocus = function() {
428 // Capture keypresses when clipboard is not focused
429 GuacamoleUI.clipboard.onblur = function() {
433 // Server copy handler
434 guac.onclipboard = function(data) {
435 GuacamoleUI.clipboard.value = data;
438 GuacamoleUI.keyboard.onkeypress = function(keysym) {
439 guac.sendKeyEvent(1, keysym);
442 GuacamoleUI.keyboard.onkeyrelease = function(keysym) {
443 guac.sendKeyEvent(0, keysym);
446 // Send Ctrl-Alt-Delete
447 GuacamoleUI.buttons.ctrlAltDelete.onclick = function() {
449 var KEYSYM_CTRL = 0xFFE3;
450 var KEYSYM_ALT = 0xFFE9;
451 var KEYSYM_DELETE = 0xFFFF;
453 guac.sendKeyEvent(1, KEYSYM_CTRL);
454 guac.sendKeyEvent(1, KEYSYM_ALT);
455 guac.sendKeyEvent(1, KEYSYM_DELETE);
456 guac.sendKeyEvent(0, KEYSYM_DELETE);
457 guac.sendKeyEvent(0, KEYSYM_ALT);
458 guac.sendKeyEvent(0, KEYSYM_CTRL);