4 Guacamole - Clientless Remote Desktop
5 Copyright (C) 2010 Michael Jumper
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Affero General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Affero General Public License for more details.
17 You should have received a copy of the GNU Affero General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 <link rel="icon" type="image/png" href="images/guacamole-icon-64.png"/>
25 <link rel="stylesheet" type="text/css" href="styles/guacamole.css"/>
26 <link rel="stylesheet" type="text/css" href="styles/keyboard.css"/>
27 <title>Guacamole</title>
33 <div id="login-dialog-middle">
36 <img src="images/login-logo.png" alt="\_GUAC_/"/>
39 <div id="login-dialog">
41 <h1>Guacamole Login</h1>
43 <p id="login-error"></p>
45 <form id="login-form" action="login" method="post">
46 <table id="login-fields">
49 <td><input type="text" name="username" id="username"/></td>
53 <td><input type="password" name="password" id="password"/></td>
58 <input type="submit" name="login" id="login" value="Login"/>
66 <!-- Main UI - hidden until login succeeds -->
67 <div id="main-guacamole-ui" style="display: none">
73 <button id="showClipboard">Show Clipboard</button>
74 <div id="clipboardDiv">
77 Text copied/cut within Guacamole will appear here. Changes to the text will affect the remote clipboard, and will be pastable within the remote desktop. Use the textbox below as an interface between the client and server clipboards.
79 <textarea rows="10" cols="40" id="clipboard"></textarea>
82 <button id="showKeyboard">Show Keyboard</button>
83 <button id="CtrlAltDelete">Ctrl-Alt-Delete</button>
85 <!-- Logo and status -->
86 <img id="logo" src="images/guacamole-logo.png" alt="Guacamole" title="__GUAC_VERSION"/>
87 <span id="state"></span>
89 <a href="agpl-3.0-standalone.html"><img id="license" src="images/agpl-logo.png" alt="AGPLv3"/></a>
94 <div id="display" class="guac-display guac-loading">
95 <!-- On-screen keyboard -->
96 <div id="keyboardContainer"></div>
101 <div id="errorDialog" class="errorDialogOuter">
102 <div class="errorDialogMiddle">
103 <div class="errorDialog">
105 <p id="errorText"></p>
106 <div class="buttons"><button id="reconnect">Reconnect</button></div>
114 <script type="text/javascript" src="guacamole-common-js/keymap.js"></script>
115 <script type="text/javascript" src="guacamole-common-js/keyboard.js"></script>
116 <script type="text/javascript" src="guacamole-common-js/mouse.js"></script>
117 <script type="text/javascript" src="guacamole-common-js/layer.js"></script>
118 <script type="text/javascript" src="guacamole-common-js/guacamole.js"></script>
119 <script type="text/javascript" src="guacamole-common-js/oskeyboard.js"></script>
122 <script type="text/javascript"> /* <![CDATA[ */
124 var loginForm = document.getElementById("login-form");
125 var loginUI = document.getElementById("login-ui");
127 loginForm.onsubmit = function() {
129 var username = document.getElementById("username");
130 var password = document.getElementById("password");
133 "username=" + encodeURIComponent(username.value)
134 + "&password=" + encodeURIComponent(password.value)
136 var xmlhttprequest = new XMLHttpRequest();
137 xmlhttprequest.open("POST", "login", false);
138 xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
139 xmlhttprequest.setRequestHeader("Content-length", data.length);
140 xmlhttprequest.send(data);
142 if (xmlhttprequest.status == 200) {
143 loginUI.style.display = "none";
148 var loginError = document.getElementById("login-error");
150 // Display error, reset and refocus password field
151 loginError.textContent = "Invalid login. Please try again.";
161 // Shows guacamole interface and initiates connection to guacamole
162 function startGuacamole() {
164 document.getElementById("main-guacamole-ui").style.display = "block";
166 var menu = document.getElementById("menu");
167 var display = document.getElementById("display");
168 var logo = document.getElementById("logo");
170 var errorDialog = document.getElementById("errorDialog");
171 var errorDialogText = document.getElementById("errorText");
173 // Position display correctly
174 window.onresize = function() {
175 display.style.top = menu.offsetHeight + "px";
180 // Instantiate client
181 var guac = new GuacamoleClient(display, "tunnel");
183 var state = document.getElementById("state");
184 guac.setOnStateChangeHandler(function(clientState) {
186 switch (clientState) {
188 state.textContent = "Idle."
191 state.textContent = "Connecting...";
194 state.textContent = "Connected, waiting for first update...";
197 display.className = display.className.replace(/guac-loading/, '');
198 menu.className = "connected";
199 state.textContent = "Connected.";
202 state.textContent = "Disconnecting...";
205 state.textContent = "Disconnected.";
208 state.textContent = "Unknown";
212 // Cache error image (might not be available when error occurs)
213 var guacErrorImage = new Image();
214 guacErrorImage.src = "images/noguacamole-logo.png";
216 guac.setErrorHandler(function(error) {
218 menu.className = "error";
219 display.className += " guac-error";
221 logo.src = guacErrorImage.src;
222 errorDialogText.textContent = error;
223 errorDialog.style.visibility = "visible";
228 var reconnect = document.getElementById("reconnect");
229 reconnect.onclick = function() {
230 window.location.reload();
236 // Disconnect on close
237 window.onunload = function() {
241 // Handle clipboard events
242 var clipboardElement = document.getElementById("clipboard");
243 clipboardElement.onchange = function() {
245 var text = clipboardElement.value;
246 guac.setClipboard(text);
250 // Ignore keypresses when clipboard is focused
251 clipboardElement.onfocus = function() {
252 guac.disableKeyboard();
255 // Capture keypresses when clipboard is not focused
256 clipboardElement.onblur = function() {
257 guac.enableKeyboard();
260 // Server copy handler
261 guac.setClipboardHandler(
263 clipboardElement.value = data;
268 // Show/Hide clipboard
269 var clipboardDiv = document.getElementById("clipboardDiv");
270 var showClipboard = document.getElementById("showClipboard");
271 showClipboard.onclick = function() {
273 var displayed = clipboardDiv.style.display;
274 if (displayed != "block") {
275 clipboardDiv.style.display = "block";
276 showClipboard.innerHTML = "Hide Clipboard";
279 clipboardDiv.style.display = "none";
280 showClipboard.innerHTML = "Show Clipboard";
281 clipboardElement.onchange();
287 // Show/Hide keyboard
288 var keyboardContainer = document.getElementById("keyboardContainer");
289 var showKeyboard = document.getElementById("showKeyboard");
290 showKeyboard.onclick = function() {
292 var displayed = keyboardContainer.style.display;
293 if (displayed != "block") {
294 keyboardContainer.style.display = "block";
295 showKeyboard.textContent = "Hide Keyboard";
298 keyboardContainer.style.display = "none";
299 showKeyboard.textContent = "Show Keyboard";
304 // On-screen keyboard
305 var osKeyboard = new GuacamoleOnScreenKeyboard("layouts/en-us-qwerty.xml");
306 keyboardContainer.appendChild(osKeyboard);
308 osKeyboard.setKeyPressedHandler(
310 guac.pressKey(keysym);
314 osKeyboard.setKeyReleasedHandler(
316 guac.releaseKey(keysym);
320 // Send Ctrl-Alt-Delete
321 var CtrlAltDelete = document.getElementById("CtrlAltDelete");
323 CtrlAltDelete.onclick = function() {
324 guac.pressKey(KEYSYM_CTRL);
325 guac.pressKey(KEYSYM_ALT);
326 guac.pressKey(KEYSYM_DELETE);
327 guac.releaseKey(KEYSYM_DELETE);
328 guac.releaseKey(KEYSYM_ALT);
329 guac.releaseKey(KEYSYM_CTRL);