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="guacamole.css"/>
26 <link rel="stylesheet" type="text/css" href="guac-web-lib/css/guacamole.css"/>
27 <link rel="stylesheet" type="text/css" href="keyboard.css"/>
28 <title>Guacamole</title>
34 <div id="login-dialog-middle">
37 <img src="images/login-logo.png" alt="\_GUAC_/"/>
40 <div id="login-dialog">
42 <h1>Guacamole Login</h1>
44 <p id="login-error"></p>
46 <form id="login-form" action="login" method="post">
47 <table id="login-fields">
50 <td><input type="text" name="username" id="username"/></td>
54 <td><input type="password" name="password" id="password"/></td>
59 <input type="submit" name="login" id="login" value="Login"/>
67 <!-- Main UI - hidden until login succeeds -->
68 <div id="main-guacamole-ui" style="display: none">
74 <button id="showClipboard">Show Clipboard</button>
75 <div id="clipboardDiv">
78 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.
80 <textarea rows="10" cols="40" id="clipboard"></textarea>
83 <button id="showKeyboard">Show Keyboard</button>
84 <button id="CtrlAltDelete">Ctrl-Alt-Delete</button>
86 <!-- Logo and status -->
87 <img id="logo" src="images/guacamole-logo.png" alt="Guacamole" title="__GUAC_VERSION"/>
88 <span id="state"></span>
90 <a href="agpl-3.0-standalone.html"><img id="license" src="images/agpl-logo.png" alt="AGPLv3"/></a>
95 <div id="display" class="guac-display guac-loading">
96 <!-- On-screen keyboard -->
97 <div id="keyboardContainer"></div>
102 <div id="errorDialog" class="errorDialogOuter">
103 <div class="errorDialogMiddle">
104 <div class="errorDialog">
106 <p id="errorText"></p>
107 <div class="buttons"><button id="reconnect">Reconnect</button></div>
115 <script type="text/javascript" src="guac-web-lib/javascript/keymap.js"></script>
116 <script type="text/javascript" src="guac-web-lib/javascript/keyboard.js"></script>
117 <script type="text/javascript" src="guac-web-lib/javascript/mouse.js"></script>
118 <script type="text/javascript" src="guac-web-lib/javascript/layer.js"></script>
119 <script type="text/javascript" src="guac-web-lib/javascript/guacamole.js"></script>
120 <script type="text/javascript" src="guac-web-lib/javascript/oskeyboard.js"></script>
123 <script type="text/javascript"> /* <![CDATA[ */
125 var loginForm = document.getElementById("login-form");
126 var loginUI = document.getElementById("login-ui");
128 loginForm.onsubmit = function() {
130 var username = document.getElementById("username");
131 var password = document.getElementById("password");
134 "username=" + encodeURIComponent(username.value)
135 + "&password=" + encodeURIComponent(password.value)
137 var xmlhttprequest = new XMLHttpRequest();
138 xmlhttprequest.open("POST", "login", false);
139 xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
140 xmlhttprequest.setRequestHeader("Content-length", data.length);
141 xmlhttprequest.send(data);
143 if (xmlhttprequest.status == 200) {
144 loginUI.style.display = "none";
149 var loginError = document.getElementById("login-error");
151 // Display error, reset and refocus password field
152 loginError.textContent = "Invalid login. Please try again.";
162 // Shows guacamole interface and initiates connection to guacamole
163 function startGuacamole() {
165 document.getElementById("main-guacamole-ui").style.display = "block";
167 var menu = document.getElementById("menu");
168 var display = document.getElementById("display");
169 var logo = document.getElementById("logo");
171 var errorDialog = document.getElementById("errorDialog");
172 var errorDialogText = document.getElementById("errorText");
174 // Position display correctly
175 window.onresize = function() {
176 display.style.top = menu.offsetHeight + "px";
181 // Instantiate client
182 var guac = new GuacamoleClient(display);
184 var state = document.getElementById("state");
185 guac.setOnStateChangeHandler(function(clientState) {
187 switch (clientState) {
189 state.textContent = "Idle."
192 state.textContent = "Connecting...";
195 state.textContent = "Connected, waiting for first update...";
198 display.className = display.className.replace(/guac-loading/, '');
199 menu.className = "connected";
200 state.textContent = "Connected.";
203 state.textContent = "Disconnecting...";
206 state.textContent = "Disconnected.";
209 state.textContent = "Unknown";
213 // Cache error image (might not be available when error occurs)
214 var guacErrorImage = new Image();
215 guacErrorImage.src = "images/noguacamole-logo.png";
217 guac.setNameHandler(function(name) {
218 document.title = name;
221 guac.setErrorHandler(function(error) {
222 menu.className = "error";
223 logo.src = guacErrorImage.src;
224 errorDialogText.textContent = error;
225 errorDialog.style.visibility = "visible";
229 var reconnect = document.getElementById("reconnect");
230 reconnect.onclick = function() {
231 window.location.reload();
237 // Disconnect on close
238 window.onunload = function() {
242 // Handle clipboard events
243 var clipboardElement = document.getElementById("clipboard");
244 clipboardElement.onchange = function() {
246 var text = clipboardElement.value;
247 guac.setClipboard(text);
251 // Ignore keypresses when clipboard is focused
252 clipboardElement.onfocus = function() {
253 guac.disableKeyboard();
256 // Capture keypresses when clipboard is not focused
257 clipboardElement.onblur = function() {
258 guac.enableKeyboard();
261 // Server copy handler
262 guac.setClipboardHandler(
264 clipboardElement.value = data;
269 // Show/Hide clipboard
270 var clipboardDiv = document.getElementById("clipboardDiv");
271 var showClipboard = document.getElementById("showClipboard");
272 showClipboard.onclick = function() {
274 var displayed = clipboardDiv.style.display;
275 if (displayed != "block") {
276 clipboardDiv.style.display = "block";
277 showClipboard.innerHTML = "Hide Clipboard";
280 clipboardDiv.style.display = "none";
281 showClipboard.innerHTML = "Show Clipboard";
282 clipboardElement.onchange();
288 // Show/Hide keyboard
289 var keyboardContainer = document.getElementById("keyboardContainer");
290 var showKeyboard = document.getElementById("showKeyboard");
291 showKeyboard.onclick = function() {
293 var displayed = keyboardContainer.style.display;
294 if (displayed != "block") {
295 keyboardContainer.style.display = "block";
296 showKeyboard.textContent = "Hide Keyboard";
299 keyboardContainer.style.display = "none";
300 showKeyboard.textContent = "Show Keyboard";
305 // On-screen keyboard
306 var osKeyboard = new GuacamoleOnScreenKeyboard("layouts/en-us-qwerty.xml");
307 keyboardContainer.appendChild(osKeyboard);
309 osKeyboard.setKeyPressedHandler(
311 guac.pressKey(keysym);
315 osKeyboard.setKeyReleasedHandler(
317 guac.releaseKey(keysym);
321 // Send Ctrl-Alt-Delete
322 var CtrlAltDelete = document.getElementById("CtrlAltDelete");
324 CtrlAltDelete.onclick = function() {
325 guac.pressKey(KEYSYM_CTRL);
326 guac.pressKey(KEYSYM_ALT);
327 guac.pressKey(KEYSYM_DELETE);
328 guac.releaseKey(KEYSYM_DELETE);
329 guac.releaseKey(KEYSYM_ALT);
330 guac.releaseKey(KEYSYM_CTRL);