eaebf3a661ea0d2f2703788867a2878cadb9ff60
[guacamole.git] / src / main / webapp / index.xhtml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE html>
3
4 <!--
5     Guacamole - Clientless Remote Desktop
6     Copyright (C) 2010  Michael Jumper
7
8     This program is free software: you can redistribute it and/or modify
9     it under the terms of the GNU Affero General Public License as published by
10     the Free Software Foundation, either version 3 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU Affero General Public License for more details.
17
18     You should have received a copy of the GNU Affero General Public License
19     along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 -->
21
22 <html xmlns="http://www.w3.org/1999/xhtml">
23
24     <head>
25         <link rel="icon" type="image/png" href="images/guacamole-logo-64.png"/>
26         <link rel="stylesheet" type="text/css" href="styles/guacamole.css"/>
27         <link rel="stylesheet" type="text/css" href="styles/keyboard.css"/>
28         <title>Guacamole</title>
29     </head>
30
31     <body>
32
33         <div id="login-ui">
34             <div id="login-dialog-middle">
35
36                 <div id="login-dialog">
37
38                     <p id="login-error"></p>
39
40                     <form id="login-form" action="#" method="post">
41
42                         <div id="login-fields">
43                             <table>
44                                 <tr>
45                                     <th>Username</th>
46                                     <td><input type="text" name="username" id="username" autofocus="autofocus"/></td>
47                                 </tr>
48                                 <tr>
49                                     <th>Password</th>
50                                     <td><input type="password" name="password" id="password"/></td>
51                                 </tr>
52                             </table>
53
54                             <img class="logo" src="images/guacamole-logo-64.png" alt=""/>
55                         </div>
56
57                         <div id="buttons">
58                             <input type="submit" name="login" id="login" value="Login"/>
59                         </div>
60
61                     </form>
62                 </div>
63
64                 <div id="version-dialog">
65                     Guacamole ${project.version}
66                 </div>
67
68             </div>
69         </div>
70
71         <!-- Main UI - hidden until login succeeds -->
72         <div id="main-guacamole-ui" style="display: none">
73
74             <!-- Menu -->
75             <div id="menu">
76
77                 <!-- Clipboard -->
78                 <button id="showClipboard">Show Clipboard</button>
79                 <div id="clipboardDiv">
80                     <h2>Clipboard</h2>
81                     <p>
82                     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.
83                     </p>
84                     <textarea rows="10" cols="40" id="clipboard"></textarea>
85                 </div>
86
87                 <button id="showKeyboard">Show Keyboard</button>
88                 <button id="ctrlAltDelete">Ctrl-Alt-Delete</button>
89
90                 <!-- Logo and status -->
91                 <img id="status-logo" class="logo" src="images/guacamole-logo-24.png" alt="Guacamole" title="Guacamole ${project.version}"/>
92                 <span id="state"></span>
93
94                 <a href="agpl-3.0-standalone.html"><img id="license" src="images/agpl-logo.png" alt="AGPLv3"/></a>
95             </div>
96
97
98             <!-- Display -->
99             <div id="display" class="guac-display guac-loading">
100                 <!-- On-screen keyboard -->
101                 <div id="keyboardContainer"></div>
102             </div>
103
104
105             <!-- Error Dialog-->
106             <div id="errorDialog" class="errorDialogOuter">
107                 <div class="errorDialogMiddle">
108                     <div class="errorDialog">
109                         <p id="errorText"></p>
110                         <div class="buttons"><button id="reconnect">Reconnect</button></div>
111                     </div>
112                 </div>
113             </div>
114         </div>
115
116
117         <!-- Scripts -->
118         <script type="text/javascript" src="guacamole-common-js/keyboard.js"></script>
119         <script type="text/javascript" src="guacamole-common-js/mouse.js"></script>
120         <script type="text/javascript" src="guacamole-common-js/layer.js"></script>
121         <script type="text/javascript" src="guacamole-common-js/tunnel.js"></script>
122         <script type="text/javascript" src="guacamole-common-js/guacamole.js"></script>
123         <script type="text/javascript" src="guacamole-common-js/oskeyboard.js"></script>
124
125         <!-- Init -->
126         <script type="text/javascript"> /* <![CDATA[ */
127
128             var loginForm = document.getElementById("login-form");
129             var loginUI = document.getElementById("login-ui");
130             var display = document.getElementById("display");
131
132             loginForm.onsubmit = function() {
133
134                 var username = document.getElementById("username");
135                 var password = document.getElementById("password");
136
137                 var data =
138                        "username=" + encodeURIComponent(username.value)
139                     + "&password=" + encodeURIComponent(password.value)
140
141                 // Instantiate client
142                 var guac = new Guacamole.Client(
143                     display,
144                     new Guacamole.HTTPTunnel("tunnel")
145                 );
146
147                 try {
148
149                     // Connect client
150                     guac.connect(data);
151
152                 }
153                 catch (e) {
154
155                     var loginError = document.getElementById("login-error");
156
157                     // Display error, reset and refocus password field
158                     loginError.textContent = e.message;
159                     password.value = "";
160                     password.focus();
161
162                     return false;
163
164                 }
165
166                 // On success, display UI
167                 startGuacamole(guac);
168                 return false;
169
170             }
171
172             // Shows guacamole interface and initiates connection to guacamole
173             function startGuacamole(guac) {
174
175                 loginUI.style.display = "none";
176                 document.getElementById("main-guacamole-ui").style.display = "block";
177
178                 var menu = document.getElementById("menu");
179                 var logo = document.getElementById("status-logo");
180
181                 var errorDialog = document.getElementById("errorDialog");
182                 var errorDialogText = document.getElementById("errorText");
183
184                 // Position display correctly
185                 window.onresize = function() {
186                     display.style.top = menu.offsetHeight + "px";
187                 };
188
189                 window.onresize();
190
191                 var state = document.getElementById("state");
192                 guac.onstatechange = function(clientState) {
193
194                         switch (clientState) {
195                             case 0:
196                                 state.textContent = "Idle."
197                                 break;
198                             case 1:
199                                 state.textContent = "Connecting...";
200                                 break;
201                             case 2:
202                                 state.textContent = "Connected, waiting for first update...";
203                                 break;
204                             case 3:
205                                 display.className = display.className.replace(/guac-loading/, '');
206                                 menu.className = "connected";
207                                 state.textContent = "Connected.";
208                                 break;
209                             case 4:
210                                 state.textContent = "Disconnecting...";
211                                 break;
212                             case 5:
213                                 state.textContent = "Disconnected.";
214                                 break;
215                             default:
216                                 state.textContent = "Unknown";
217                         }
218                 };
219
220                 // Cache error image (might not be available when error occurs)
221                 var guacErrorImage = new Image();
222                 guacErrorImage.src = "images/noguacamole-logo-24.png";
223
224                 guac.onname = function(name) {
225                     document.title = name;
226                 };
227
228                 guac.onerror = function(error) {
229
230                     guac.disconnect();
231
232                     menu.className = "error";
233                     display.className += " guac-error";
234
235                     logo.src = guacErrorImage.src;
236                     errorDialogText.textContent = error;
237                     errorDialog.style.visibility = "visible";
238
239                     // Show error by desaturating display
240                     var layers = guac.getLayers();
241                     for (var i=0; i<layers.length; i++) {
242                         layers[i].filter(desaturateFilter);
243                     }
244
245                     // Filter for desaturation
246                     function desaturateFilter(data, width, height) {
247
248                         for (var i=0; i<data.length; i+=4) {
249
250                             // Get RGB values
251                             var r = data[i];
252                             var g = data[i+1];
253                             var b = data[i+2];
254
255                             // Desaturate
256                             var v = Math.max(r, g, b) / 2;
257                             data[i]   = v;
258                             data[i+1] = v;
259                             data[i+2] = v;
260
261                         }
262
263                     }
264
265                 };
266
267                 // Mouse
268                 var mouse = new Guacamole.Mouse(display);
269                 mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
270                     function(mouseState) {
271                         guac.sendMouseState(mouseState);
272                     };
273
274                 // Keyboard
275                 var keyboard = new Guacamole.Keyboard(document);
276
277                 function disableKeyboard() {
278                     keyboard.onkeydown = null;
279                     keyboard.onkeyup = null;
280                 }
281
282                 function enableKeyboard() {
283                     keyboard.onkeydown = 
284                         function (keysym) {
285                             guac.sendKeyEvent(1, keysym);
286                         };
287
288                     keyboard.onkeyup = 
289                         function (keysym) {
290                             guac.sendKeyEvent(0, keysym);
291                         };
292                 }
293
294                 // Enable keyboard by default
295                 enableKeyboard();
296
297                 // Reconnect button
298                 var reconnect = document.getElementById("reconnect");
299                 reconnect.onclick = function() {
300                     window.location.reload();
301                 };
302
303                 // Disconnect on close
304                 window.onunload = function() {
305                     guac.disconnect();
306                 }
307
308                 // Handle clipboard events
309                 var clipboardElement = document.getElementById("clipboard");
310                 clipboardElement.onchange = function() {
311
312                     var text = clipboardElement.value;
313                     guac.setClipboard(text);
314
315                 };
316
317                 // Ignore keypresses when clipboard is focused
318                 clipboardElement.onfocus = function() {
319                     disableKeyboard();
320                 };
321
322                 // Capture keypresses when clipboard is not focused
323                 clipboardElement.onblur = function() {
324                     enableKeyboard();
325                 };
326
327                 // Server copy handler
328                 guac.onclipboard = function(data) {
329                     clipboardElement.value = data;
330                 };
331
332
333                 // Show/Hide clipboard
334                 var clipboardDiv = document.getElementById("clipboardDiv");
335                 var showClipboard = document.getElementById("showClipboard");
336                 showClipboard.onclick = function() {
337
338                     var displayed = clipboardDiv.style.display;
339                     if (displayed != "block") {
340                         clipboardDiv.style.display = "block";
341                         showClipboard.innerHTML = "Hide Clipboard";
342                     }
343                     else {
344                         clipboardDiv.style.display = "none";
345                         showClipboard.innerHTML = "Show Clipboard";
346                         clipboardElement.onchange();
347                     }
348
349                 };
350
351
352                 // Show/Hide keyboard
353                 var keyboardContainer = document.getElementById("keyboardContainer");
354                 var showKeyboard = document.getElementById("showKeyboard");
355                 showKeyboard.onclick = function() {
356
357                     var displayed = keyboardContainer.style.display;
358                     if (displayed != "block") {
359                         keyboardContainer.style.display = "block";
360                         showKeyboard.textContent = "Hide Keyboard";
361                     }
362                     else {
363                         keyboardContainer.style.display = "none";
364                         showKeyboard.textContent = "Show Keyboard";
365                     }
366
367                 };
368
369                 // On-screen keyboard
370                 var osKeyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty.xml");
371                 keyboardContainer.appendChild(osKeyboard);
372
373                 osKeyboard.setKeyPressedHandler(
374                         function(keysym) {
375                             guac.sendKeyEvent(1, keysym);
376                         }
377                 );
378
379                 osKeyboard.setKeyReleasedHandler(
380                         function(keysym) {
381                             guac.sendKeyEvent(0, keysym);
382                         }
383                 );
384
385                 // Send Ctrl-Alt-Delete
386                 var ctrlAltDelete = document.getElementById("ctrlAltDelete");
387
388                 ctrlAltDelete.onclick = function() {
389
390                     var KEYSYM_CTRL   = 0xFF03;
391                     var KEYSYM_ALT    = 0xFFE9;
392                     var KEYSYM_DELETE = 0xFFFF;
393
394                     guac.sendKeyEvent(1, KEYSYM_CTRL);
395                     guac.sendKeyEvent(1, KEYSYM_ALT);
396                     guac.sendKeyEvent(1, KEYSYM_DELETE);
397                     guac.sendKeyEvent(0, KEYSYM_DELETE);
398                     guac.sendKeyEvent(0, KEYSYM_ALT);
399                     guac.sendKeyEvent(0, KEYSYM_CTRL);
400                 }
401
402             }
403
404             /* ]]> */ </script>
405
406     </body>
407
408 </html>