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.setErrorHandler(function(error) {
218 menu.className = "error";
219 logo.src = guacErrorImage.src;
220 errorDialogText.textContent = error;
221 errorDialog.style.visibility = "visible";
225 var reconnect = document.getElementById("reconnect");
226 reconnect.onclick = function() {
227 window.location.reload();
233 // Disconnect on close
234 window.onunload = function() {
238 // Handle clipboard events
239 var clipboardElement = document.getElementById("clipboard");
240 clipboardElement.onchange = function() {
242 var text = clipboardElement.value;
243 guac.setClipboard(text);
247 // Ignore keypresses when clipboard is focused
248 clipboardElement.onfocus = function() {
249 guac.disableKeyboard();
252 // Capture keypresses when clipboard is not focused
253 clipboardElement.onblur = function() {
254 guac.enableKeyboard();
257 // Server copy handler
258 guac.setClipboardHandler(
260 clipboardElement.value = data;
265 // Show/Hide clipboard
266 var clipboardDiv = document.getElementById("clipboardDiv");
267 var showClipboard = document.getElementById("showClipboard");
268 showClipboard.onclick = function() {
270 var displayed = clipboardDiv.style.display;
271 if (displayed != "block") {
272 clipboardDiv.style.display = "block";
273 showClipboard.innerHTML = "Hide Clipboard";
276 clipboardDiv.style.display = "none";
277 showClipboard.innerHTML = "Show Clipboard";
278 clipboardElement.onchange();
284 // Show/Hide keyboard
285 var keyboardContainer = document.getElementById("keyboardContainer");
286 var showKeyboard = document.getElementById("showKeyboard");
287 showKeyboard.onclick = function() {
289 var displayed = keyboardContainer.style.display;
290 if (displayed != "block") {
291 keyboardContainer.style.display = "block";
292 showKeyboard.textContent = "Hide Keyboard";
295 keyboardContainer.style.display = "none";
296 showKeyboard.textContent = "Show Keyboard";
301 // On-screen keyboard
302 var osKeyboard = new GuacamoleOnScreenKeyboard("layouts/en-us-qwerty.xml");
303 keyboardContainer.appendChild(osKeyboard);
305 osKeyboard.setKeyPressedHandler(
307 guac.pressKey(keysym);
311 osKeyboard.setKeyReleasedHandler(
313 guac.releaseKey(keysym);
317 // Send Ctrl-Alt-Delete
318 var CtrlAltDelete = document.getElementById("CtrlAltDelete");
320 CtrlAltDelete.onclick = function() {
321 guac.pressKey(KEYSYM_CTRL);
322 guac.pressKey(KEYSYM_ALT);
323 guac.pressKey(KEYSYM_DELETE);
324 guac.releaseKey(KEYSYM_DELETE);
325 guac.releaseKey(KEYSYM_ALT);
326 guac.releaseKey(KEYSYM_CTRL);