Filter webapp/* so we don't have to hard-code the version number. Display version...
[guacamole.git] / src / main / webapp / index.html
1 <!DOCTYPE HTML>
2
3 <!--
4     Guacamole - Clientless Remote Desktop
5     Copyright (C) 2010  Michael Jumper
6
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.
11
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.
16
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/>.
19 -->
20
21 <html>
22
23     <head>
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>
28     </head>
29
30     <body>
31
32         <div id="login-ui">
33             <div id="login-dialog-middle">
34
35                 <div id="login-logo">
36                     <img src="images/login-logo.png" alt="\_GUAC_/"/>
37                 </div>
38
39                 <div id="login-dialog">
40
41                     <h1>Guacamole Login</h1>
42
43                     <p id="login-error"></p>
44
45                     <form id="login-form" action="#" method="post">
46                         <table id="login-fields">
47                             <tr>
48                                 <th>Username</th>
49                                 <td><input type="text" name="username" id="username"/></td>
50                             </tr>
51                             <tr>
52                                 <th>Password</th>
53                                 <td><input type="password" name="password" id="password"/></td>
54                             </tr>
55                         </table>
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">Guacamole ${project.version}</div>
65
66             </div>
67         </div>
68
69         <!-- Main UI - hidden until login succeeds -->
70         <div id="main-guacamole-ui" style="display: none">
71
72             <!-- Menu -->
73             <div id="menu">
74
75                 <!-- Clipboard -->
76                 <button id="showClipboard">Show Clipboard</button>
77                 <div id="clipboardDiv">
78                     <h2>Clipboard</h2>
79                     <p>
80                     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.
81                     </p>
82                     <textarea rows="10" cols="40" id="clipboard"></textarea>
83                 </div>
84
85                 <button id="showKeyboard">Show Keyboard</button>
86                 <button id="CtrlAltDelete">Ctrl-Alt-Delete</button>
87
88                 <!-- Logo and status -->
89                 <img id="logo" src="images/guacamole-logo.png" alt="Guacamole" title="Guacamole ${project.version}"/>
90                 <span id="state"></span>
91
92                 <a href="agpl-3.0-standalone.html"><img id="license" src="images/agpl-logo.png" alt="AGPLv3"/></a>
93             </div>
94
95
96             <!-- Display -->
97             <div id="display" class="guac-display guac-loading">
98                 <!-- On-screen keyboard -->
99                 <div id="keyboardContainer"></div>
100             </div>
101
102
103             <!-- Error Dialog-->
104             <div id="errorDialog" class="errorDialogOuter">
105                 <div class="errorDialogMiddle">
106                     <div class="errorDialog">
107                         <h1>Error</h1>
108                         <p id="errorText"></p>
109                         <div class="buttons"><button id="reconnect">Reconnect</button></div>
110                     </div>
111                 </div>
112             </div>
113         </div>
114
115
116         <!-- Scripts -->
117         <script type="text/javascript" src="guacamole-common-js/keymap.js"></script>
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 GuacamoleClient(
143                     display,
144                     new GuacamoleHTTPTunnel("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("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.setOnStateChangeHandler(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.png";
223
224                 guac.setNameHandler(function(name) {
225                     document.title = name;
226                 });
227
228                 guac.setErrorHandler(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 GuacamoleMouse(display);
269                 mouse.setButtonPressedHandler(
270                     function(mouseState) {
271                         guac.sendMouseState(mouseState);
272                     }
273                 );
274
275                 mouse.setButtonReleasedHandler(
276                     function(mouseState) {
277                         guac.sendMouseState(mouseState);
278                     }
279                 );
280
281                 mouse.setMovementHandler(
282                     function(mouseState) {
283                         guac.sendMouseState(mouseState);
284                     }
285                 );
286
287                 // Keyboard
288                 var keyboard = new GuacamoleKeyboard(document);
289
290                 function disableKeyboard() {
291                     keyboard.setKeyPressedHandler(null);
292                     keyboard.setKeyReleasedHandler(null);
293                 }
294
295                 function enableKeyboard() {
296                     keyboard.setKeyPressedHandler(
297                         function (keysym) {
298                             guac.sendKeyEvent(1, keysym);
299                         }
300                     );
301
302                     keyboard.setKeyReleasedHandler(
303                         function (keysym) {
304                             guac.sendKeyEvent(0, keysym);
305                         }
306                     );
307                 }
308
309                 // Enable keyboard by default
310                 enableKeyboard();
311
312                 // Reconnect button
313                 var reconnect = document.getElementById("reconnect");
314                 reconnect.onclick = function() {
315                     window.location.reload();
316                 };
317
318                 // Disconnect on close
319                 window.onunload = function() {
320                     guac.disconnect();
321                 }
322
323                 // Handle clipboard events
324                 var clipboardElement = document.getElementById("clipboard");
325                 clipboardElement.onchange = function() {
326
327                     var text = clipboardElement.value;
328                     guac.setClipboard(text);
329
330                 };
331
332                 // Ignore keypresses when clipboard is focused
333                 clipboardElement.onfocus = function() {
334                     disableKeyboard();
335                 };
336
337                 // Capture keypresses when clipboard is not focused
338                 clipboardElement.onblur = function() {
339                     enableKeyboard();
340                 };
341
342                 // Server copy handler
343                 guac.setClipboardHandler(
344                     function(data) {
345                         clipboardElement.value = data;
346                     }
347                 );
348
349
350                 // Show/Hide clipboard
351                 var clipboardDiv = document.getElementById("clipboardDiv");
352                 var showClipboard = document.getElementById("showClipboard");
353                 showClipboard.onclick = function() {
354
355                     var displayed = clipboardDiv.style.display;
356                     if (displayed != "block") {
357                         clipboardDiv.style.display = "block";
358                         showClipboard.innerHTML = "Hide Clipboard";
359                     }
360                     else {
361                         clipboardDiv.style.display = "none";
362                         showClipboard.innerHTML = "Show Clipboard";
363                         clipboardElement.onchange();
364                     }
365
366                 };
367
368
369                 // Show/Hide keyboard
370                 var keyboardContainer = document.getElementById("keyboardContainer");
371                 var showKeyboard = document.getElementById("showKeyboard");
372                 showKeyboard.onclick = function() {
373
374                     var displayed = keyboardContainer.style.display;
375                     if (displayed != "block") {
376                         keyboardContainer.style.display = "block";
377                         showKeyboard.textContent = "Hide Keyboard";
378                     }
379                     else {
380                         keyboardContainer.style.display = "none";
381                         showKeyboard.textContent = "Show Keyboard";
382                     }
383
384                 };
385
386                 // On-screen keyboard
387                 var osKeyboard = new GuacamoleOnScreenKeyboard("layouts/en-us-qwerty.xml");
388                 keyboardContainer.appendChild(osKeyboard);
389
390                 osKeyboard.setKeyPressedHandler(
391                         function(keysym) {
392                             guac.sendKeyEvent(1, keysym);
393                         }
394                 );
395
396                 osKeyboard.setKeyReleasedHandler(
397                         function(keysym) {
398                             guac.sendKeyEvent(0, keysym);
399                         }
400                 );
401
402                 // Send Ctrl-Alt-Delete
403                 var CtrlAltDelete = document.getElementById("CtrlAltDelete");
404
405                 CtrlAltDelete.onclick = function() {
406                     guac.sendKeyEvent(1, KEYSYM_CTRL);
407                     guac.sendKeyEvent(1, KEYSYM_ALT);
408                     guac.sendKeyEvent(1, KEYSYM_DELETE);
409                     guac.sendKeyEvent(0, KEYSYM_DELETE);
410                     guac.sendKeyEvent(0, KEYSYM_ALT);
411                     guac.sendKeyEvent(0, KEYSYM_CTRL);
412                 }
413
414             }
415
416             /* ]]> */ </script>
417
418     </body>
419
420 </html>