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 / 5) + 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 GuacamoleUI.buttons.showKeyboard.onclick = function() {
124 var displayed = GuacamoleUI.containers.keyboard.style.display;
125 if (displayed != "block") {
126 GuacamoleUI.containers.keyboard.style.display = "block";
127 GuacamoleUI.buttons.showKeyboard.textContent = "Hide Keyboard";
130 GuacamoleUI.containers.keyboard.style.display = "none";
131 GuacamoleUI.buttons.showKeyboard.textContent = "Show Keyboard";
137 GuacamoleUI.buttons.logout.onclick = function() {
138 window.location.href = "logout";
141 var detectMenuOpenTimeout = null;
142 var detectMenuCloseTimeout = null;
144 GuacamoleUI.menu.addEventListener('mouseover', function() {
146 // If we were waiting for menu close, we're not anymore
147 if (detectMenuCloseTimeout != null) {
148 window.clearTimeout(detectMenuCloseTimeout);
149 detectMenuCloseTimeout = null;
154 GuacamoleUI.menu.addEventListener('mouseout', function(e) {
156 // Get parent of the element the mouse pointer is leaving
157 if (!e) e = window.event;
158 var target = e.relatedTarget || e.toElement;
160 // Ensure target is not menu nor child of menu
161 var targetParent = target;
162 while (targetParent != null) {
163 if (targetParent == GuacamoleUI.menu) return;
164 targetParent = targetParent.parentNode;
167 // If not already waiting, start detection of mouse leave
168 if (detectMenuCloseTimeout == null) {
169 detectMenuCloseTimeout = window.setTimeout(function() {
170 GuacamoleUI.shadeMenu();
171 detectMenuCloseTimeout = null;
177 // When mouse hovers over top of screen, start detection of mouse hover
178 GuacamoleUI.menuControl.addEventListener('mousemove', function() {
180 // If we were waiting for menu close, we're not anymore
181 if (detectMenuCloseTimeout != null) {
182 window.clearTimeout(detectMenuCloseTimeout);
183 detectMenuCloseTimeout = null;
186 // Clear old timeout if mouse moved while we were waiting
187 if (detectMenuOpenTimeout != null) {
188 window.clearTimeout(detectMenuOpenTimeout);
189 detectMenuOpenTimeout = null;
192 // If not alread waiting, wait for 250ms before showing menu
193 detectMenuOpenTimeout = window.setTimeout(function() {
194 GuacamoleUI.showMenu();
195 detectMenuOpenTimeout = null;
200 // When mouse leaves top of screen, cancel showing the menu
201 GuacamoleUI.menuControl.addEventListener('mouseout', function() {
203 // If we were waiting for menu open, we're not anymore
204 if (detectMenuOpenTimeout != null) {
205 window.clearTimeout(detectMenuOpenTimeout);
206 detectMenuCloseTimeout = null;
209 // If not already waiting, start detection of mouse leave
210 if (detectMenuCloseTimeout == null) {
211 detectMenuCloseTimeout = window.setTimeout(function() {
212 GuacamoleUI.shadeMenu();
213 detectMenuCloseTimeout = null;
221 GuacamoleUI.buttons.reconnect.onclick = function() {
222 window.location.reload();
225 // On-screen keyboard
226 GuacamoleUI.keyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty.xml");
227 GuacamoleUI.containers.keyboard.appendChild(GuacamoleUI.keyboard);
231 // Tie UI events / behavior to a specific Guacamole client
232 GuacamoleUI.attach = function(guac) {
235 var mouse = new Guacamole.Mouse(GuacamoleUI.display);
236 mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
237 function(mouseState) {
238 guac.sendMouseState(mouseState);
242 var keyboard = new Guacamole.Keyboard(document);
244 function disableKeyboard() {
245 keyboard.onkeydown = null;
246 keyboard.onkeyup = null;
249 function enableKeyboard() {
252 guac.sendKeyEvent(1, keysym);
257 guac.sendKeyEvent(0, keysym);
261 // Enable keyboard by default
264 // Handle client state change
265 guac.onstatechange = function(clientState) {
266 switch (clientState) {
270 GuacamoleUI.state.textContent = "Idle."
275 GuacamoleUI.state.textContent = "Connecting...";
278 // Connected + waiting
280 GuacamoleUI.state.textContent = "Connected, waiting for first update...";
286 GuacamoleUI.display.className =
287 GuacamoleUI.display.className.replace(/guac-loading/, '');
289 GuacamoleUI.menu.className = "connected";
290 GuacamoleUI.state.textContent = "Connected.";
291 GuacamoleUI.shadeMenu();
296 GuacamoleUI.state.textContent = "Disconnecting...";
301 GuacamoleUI.state.textContent = "Disconnected.";
304 // Unknown status code
306 GuacamoleUI.state.textContent = "Unknown";
311 // Name instruction handler
312 guac.onname = function(name) {
313 document.title = name;
317 guac.onerror = function(error) {
319 // Disconnect, if connected
322 // Display error message
323 GuacamoleUI.showError(error);
325 // Show error by desaturating display
326 var layers = guac.getLayers();
327 for (var i=0; i<layers.length; i++) {
328 layers[i].filter(desaturateFilter);
331 // Filter for desaturation
332 function desaturateFilter(data, width, height) {
334 for (var i=0; i<data.length; i+=4) {
342 var v = Math.max(r, g, b) / 2;
353 // Disconnect on close
354 window.onunload = function() {
358 // Handle clipboard events
359 GuacamoleUI.clipboard.onchange = function() {
361 var text = GuacamoleUI.clipboard.value;
362 guac.setClipboard(text);
366 // Ignore keypresses when clipboard is focused
367 GuacamoleUI.clipboard.onfocus = function() {
371 // Capture keypresses when clipboard is not focused
372 GuacamoleUI.clipboard.onblur = function() {
376 // Server copy handler
377 guac.onclipboard = function(data) {
378 GuacamoleUI.clipboard.value = data;
381 GuacamoleUI.keyboard.setKeyPressedHandler(
383 guac.sendKeyEvent(1, keysym);
387 GuacamoleUI.keyboard.setKeyReleasedHandler(
389 guac.sendKeyEvent(0, keysym);
393 // Send Ctrl-Alt-Delete
394 GuacamoleUI.buttons.ctrlAltDelete.onclick = function() {
396 var KEYSYM_CTRL = 0xFFE3;
397 var KEYSYM_ALT = 0xFFE9;
398 var KEYSYM_DELETE = 0xFFFF;
400 guac.sendKeyEvent(1, KEYSYM_CTRL);
401 guac.sendKeyEvent(1, KEYSYM_ALT);
402 guac.sendKeyEvent(1, KEYSYM_DELETE);
403 guac.sendKeyEvent(0, KEYSYM_DELETE);
404 guac.sendKeyEvent(0, KEYSYM_ALT);
405 guac.sendKeyEvent(0, KEYSYM_CTRL);