Using UTF-8 source encoding.
[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="login" 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                     </form>
61                 </div>
62
63             </div>
64         </div>
65
66         <!-- Main UI - hidden until login succeeds -->
67         <div id="main-guacamole-ui" style="display: none">
68
69             <!-- Menu -->
70             <div id="menu">
71
72                 <!-- Clipboard -->
73                 <button id="showClipboard">Show Clipboard</button>
74                 <div id="clipboardDiv">
75                     <h2>Clipboard</h2>
76                     <p>
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.
78                     </p>
79                     <textarea rows="10" cols="40" id="clipboard"></textarea>
80                 </div>
81
82                 <button id="showKeyboard">Show Keyboard</button>
83                 <button id="CtrlAltDelete">Ctrl-Alt-Delete</button>
84
85                 <!-- Logo and status -->
86                 <img id="logo" src="images/guacamole-logo.png" alt="Guacamole" title="__GUAC_VERSION"/>
87                 <span id="state"></span>
88
89                 <a href="agpl-3.0-standalone.html"><img id="license" src="images/agpl-logo.png" alt="AGPLv3"/></a>
90             </div>
91
92
93             <!-- Display -->
94             <div id="display" class="guac-display guac-loading">
95                 <!-- On-screen keyboard -->
96                 <div id="keyboardContainer"></div>
97             </div>
98
99
100             <!-- Error Dialog-->
101             <div id="errorDialog" class="errorDialogOuter">
102                 <div class="errorDialogMiddle">
103                     <div class="errorDialog">
104                         <h1>Error</h1>
105                         <p id="errorText"></p>
106                         <div class="buttons"><button id="reconnect">Reconnect</button></div>
107                     </div>
108                 </div>
109             </div>
110         </div>
111
112
113         <!-- Scripts -->
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>
120
121         <!-- Init -->
122         <script type="text/javascript"> /* <![CDATA[ */
123
124             var loginForm = document.getElementById("login-form");
125             var loginUI = document.getElementById("login-ui");
126
127             loginForm.onsubmit = function() {
128
129                 var username = document.getElementById("username");
130                 var password = document.getElementById("password");
131
132                 var data =
133                        "username=" + encodeURIComponent(username.value)
134                     + "&password=" + encodeURIComponent(password.value)
135
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);
141
142                 if (xmlhttprequest.status == 200) {
143                     loginUI.style.display = "none";
144                     startGuacamole();
145                 }
146                 else {
147
148                     var loginError = document.getElementById("login-error");
149
150                     // Display error, reset and refocus password field
151                     loginError.textContent = "Invalid login. Please try again.";
152                     password.value = "";
153                     password.focus();
154
155                 }
156
157                 return false;
158
159             }
160
161             // Shows guacamole interface and initiates connection to guacamole
162             function startGuacamole() {
163
164                 document.getElementById("main-guacamole-ui").style.display = "block";
165
166                 var menu = document.getElementById("menu");
167                 var display = document.getElementById("display");
168                 var logo = document.getElementById("logo");
169
170                 var errorDialog = document.getElementById("errorDialog");
171                 var errorDialogText = document.getElementById("errorText");
172
173                 // Position display correctly
174                 window.onresize = function() {
175                     display.style.top = menu.offsetHeight + "px";
176                 };
177
178                 window.onresize();
179
180                 // Instantiate client
181                 var guac = new GuacamoleClient(display, "tunnel");
182
183                 var state = document.getElementById("state");
184                 guac.setOnStateChangeHandler(function(clientState) {
185
186                         switch (clientState) {
187                             case 0:
188                                 state.textContent = "Idle."
189                                 break;
190                             case 1:
191                                 state.textContent = "Connecting...";
192                                 break;
193                             case 2:
194                                 state.textContent = "Connected, waiting for first update...";
195                                 break;
196                             case 3:
197                                 display.className = display.className.replace(/guac-loading/, '');
198                                 menu.className = "connected";
199                                 state.textContent = "Connected.";
200                                 break;
201                             case 4:
202                                 state.textContent = "Disconnecting...";
203                                 break;
204                             case 5:
205                                 state.textContent = "Disconnected.";
206                                 break;
207                             default:
208                                 state.textContent = "Unknown";
209                         }
210                 });
211
212                 // Cache error image (might not be available when error occurs)
213                 var guacErrorImage = new Image();
214                 guacErrorImage.src = "images/noguacamole-logo.png";
215
216                 guac.setErrorHandler(function(error) {
217
218                     menu.className = "error";
219                     display.className += " guac-error";
220
221                     logo.src = guacErrorImage.src;
222                     errorDialogText.textContent = error;
223                     errorDialog.style.visibility = "visible";
224
225                 });
226
227                 // Reconnect button
228                 var reconnect = document.getElementById("reconnect");
229                 reconnect.onclick = function() {
230                     window.location.reload();
231                 };
232
233                 // Connect
234                 guac.connect();
235
236                 // Disconnect on close
237                 window.onunload = function() {
238                     guac.disconnect();
239                 }
240
241                 // Handle clipboard events
242                 var clipboardElement = document.getElementById("clipboard");
243                 clipboardElement.onchange = function() {
244
245                     var text = clipboardElement.value;
246                     guac.setClipboard(text);
247
248                 };
249
250                 // Ignore keypresses when clipboard is focused
251                 clipboardElement.onfocus = function() {
252                     guac.disableKeyboard();
253                 };
254
255                 // Capture keypresses when clipboard is not focused
256                 clipboardElement.onblur = function() {
257                     guac.enableKeyboard();
258                 };
259
260                 // Server copy handler
261                 guac.setClipboardHandler(
262                     function(data) {
263                         clipboardElement.value = data;
264                     }
265                 );
266
267
268                 // Show/Hide clipboard
269                 var clipboardDiv = document.getElementById("clipboardDiv");
270                 var showClipboard = document.getElementById("showClipboard");
271                 showClipboard.onclick = function() {
272
273                     var displayed = clipboardDiv.style.display;
274                     if (displayed != "block") {
275                         clipboardDiv.style.display = "block";
276                         showClipboard.innerHTML = "Hide Clipboard";
277                     }
278                     else {
279                         clipboardDiv.style.display = "none";
280                         showClipboard.innerHTML = "Show Clipboard";
281                         clipboardElement.onchange();
282                     }
283
284                 };
285
286
287                 // Show/Hide keyboard
288                 var keyboardContainer = document.getElementById("keyboardContainer");
289                 var showKeyboard = document.getElementById("showKeyboard");
290                 showKeyboard.onclick = function() {
291
292                     var displayed = keyboardContainer.style.display;
293                     if (displayed != "block") {
294                         keyboardContainer.style.display = "block";
295                         showKeyboard.textContent = "Hide Keyboard";
296                     }
297                     else {
298                         keyboardContainer.style.display = "none";
299                         showKeyboard.textContent = "Show Keyboard";
300                     }
301
302                 };
303
304                 // On-screen keyboard
305                 var osKeyboard = new GuacamoleOnScreenKeyboard("layouts/en-us-qwerty.xml");
306                 keyboardContainer.appendChild(osKeyboard);
307
308                 osKeyboard.setKeyPressedHandler(
309                         function(keysym) {
310                             guac.pressKey(keysym);
311                         }
312                 );
313
314                 osKeyboard.setKeyReleasedHandler(
315                         function(keysym) {
316                             guac.releaseKey(keysym);
317                         }
318                 );
319
320                 // Send Ctrl-Alt-Delete
321                 var CtrlAltDelete = document.getElementById("CtrlAltDelete");
322
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);
330                 }
331
332             }
333
334             /* ]]> */ </script>
335
336     </body>
337
338 </html>