Removed excessive escaping.
[guacamole.git] / src / main / webapp / client.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/client.css"/>
27         <link rel="stylesheet" type="text/css" href="styles/keyboard.css"/>
28         <title>Guacamole ${project.version}</title>
29     </head>
30
31     <body>
32
33         <!-- Menu -->
34         <div id="menu">
35
36             <!-- Clipboard -->
37             <button id="showClipboard">Show Clipboard</button>
38             <div id="clipboardDiv">
39                 <h2>Clipboard</h2>
40                 <p>
41                 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.
42                 </p>
43                 <textarea rows="10" cols="40" id="clipboard"></textarea>
44             </div>
45
46             <button id="showKeyboard">Show Keyboard</button>
47             <button id="ctrlAltDelete">Ctrl-Alt-Delete</button>
48             <button id="logout">Logout</button>
49
50             <!-- Logo and status -->
51             <img id="status-logo" class="logo" src="images/guacamole-logo-24.png" alt="Guacamole" title="Guacamole ${project.version}"/>
52             <span id="state"></span>
53
54         </div>
55
56
57         <!-- Display -->
58         <div id="display" class="guac-display guac-loading">
59             <!-- On-screen keyboard -->
60             <div id="keyboardContainer"></div>
61         </div>
62
63
64         <!-- Error Dialog-->
65         <div id="errorDialog" class="errorDialogOuter">
66             <div class="errorDialogMiddle">
67                 <div class="errorDialog">
68                     <p id="errorText"></p>
69                     <div class="buttons"><button id="reconnect">Reconnect</button></div>
70                 </div>
71             </div>
72         </div>
73
74
75         <!-- Scripts -->
76         <script type="text/javascript" src="guacamole-common-js/keyboard.js"></script>
77         <script type="text/javascript" src="guacamole-common-js/mouse.js"></script>
78         <script type="text/javascript" src="guacamole-common-js/layer.js"></script>
79         <script type="text/javascript" src="guacamole-common-js/tunnel.js"></script>
80         <script type="text/javascript" src="guacamole-common-js/guacamole.js"></script>
81         <script type="text/javascript" src="guacamole-common-js/oskeyboard.js"></script>
82
83         <!-- Init -->
84         <script type="text/javascript"> /* <![CDATA[ */
85
86             var display = document.getElementById("display");
87
88             // Instantiate client
89             var guac = new Guacamole.Client(
90                 display,
91                 new Guacamole.HTTPTunnel("tunnel")
92             );
93
94             var menu = document.getElementById("menu");
95             var logo = document.getElementById("status-logo");
96
97             var errorDialog = document.getElementById("errorDialog");
98             var errorDialogText = document.getElementById("errorText");
99
100             // Position display correctly
101             window.onresize = function() {
102                 display.style.top = menu.offsetHeight + "px";
103             };
104
105             window.onresize();
106
107             var state = document.getElementById("state");
108             guac.onstatechange = function(clientState) {
109
110                     switch (clientState) {
111                         case 0:
112                             state.textContent = "Idle."
113                             break;
114                         case 1:
115                             state.textContent = "Connecting...";
116                             break;
117                         case 2:
118                             state.textContent = "Connected, waiting for first update...";
119                             break;
120                         case 3:
121                             display.className = display.className.replace(/guac-loading/, '');
122                             menu.className = "connected";
123                             state.textContent = "Connected.";
124                             break;
125                         case 4:
126                             state.textContent = "Disconnecting...";
127                             break;
128                         case 5:
129                             state.textContent = "Disconnected.";
130                             break;
131                         default:
132                             state.textContent = "Unknown";
133                     }
134             };
135
136             // Cache error image (might not be available when error occurs)
137             var guacErrorImage = new Image();
138             guacErrorImage.src = "images/noguacamole-logo-24.png";
139
140             guac.onname = function(name) {
141                 document.title = name;
142             };
143
144             guac.onerror = function(error) {
145
146                 guac.disconnect();
147
148                 menu.className = "error";
149                 display.className += " guac-error";
150
151                 logo.src = guacErrorImage.src;
152                 errorDialogText.textContent = error;
153                 errorDialog.style.visibility = "visible";
154
155                 // Show error by desaturating display
156                 var layers = guac.getLayers();
157                 for (var i=0; i<layers.length; i++) {
158                     layers[i].filter(desaturateFilter);
159                 }
160
161                 // Filter for desaturation
162                 function desaturateFilter(data, width, height) {
163
164                     for (var i=0; i<data.length; i+=4) {
165
166                         // Get RGB values
167                         var r = data[i];
168                         var g = data[i+1];
169                         var b = data[i+2];
170
171                         // Desaturate
172                         var v = Math.max(r, g, b) / 2;
173                         data[i]   = v;
174                         data[i+1] = v;
175                         data[i+2] = v;
176
177                     }
178
179                 }
180
181             };
182
183             // Mouse
184             var mouse = new Guacamole.Mouse(display);
185             mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
186                 function(mouseState) {
187                     guac.sendMouseState(mouseState);
188                 };
189
190             // Keyboard
191             var keyboard = new Guacamole.Keyboard(document);
192
193             function disableKeyboard() {
194                 keyboard.onkeydown = null;
195                 keyboard.onkeyup = null;
196             }
197
198             function enableKeyboard() {
199                 keyboard.onkeydown = 
200                     function (keysym) {
201                         guac.sendKeyEvent(1, keysym);
202                     };
203
204                 keyboard.onkeyup = 
205                     function (keysym) {
206                         guac.sendKeyEvent(0, keysym);
207                     };
208             }
209
210             // Enable keyboard by default
211             enableKeyboard();
212
213             // Reconnect button
214             var reconnect = document.getElementById("reconnect");
215             reconnect.onclick = function() {
216                 window.location.reload();
217             };
218
219             // Disconnect on close
220             window.onunload = function() {
221                 guac.disconnect();
222             }
223
224             // Handle clipboard events
225             var clipboardElement = document.getElementById("clipboard");
226             clipboardElement.onchange = function() {
227
228                 var text = clipboardElement.value;
229                 guac.setClipboard(text);
230
231             };
232
233             // Ignore keypresses when clipboard is focused
234             clipboardElement.onfocus = function() {
235                 disableKeyboard();
236             };
237
238             // Capture keypresses when clipboard is not focused
239             clipboardElement.onblur = function() {
240                 enableKeyboard();
241             };
242
243             // Server copy handler
244             guac.onclipboard = function(data) {
245                 clipboardElement.value = data;
246             };
247
248
249             // Show/Hide clipboard
250             var clipboardDiv = document.getElementById("clipboardDiv");
251             var showClipboard = document.getElementById("showClipboard");
252             showClipboard.onclick = function() {
253
254                 var displayed = clipboardDiv.style.display;
255                 if (displayed != "block") {
256                     clipboardDiv.style.display = "block";
257                     showClipboard.innerHTML = "Hide Clipboard";
258                 }
259                 else {
260                     clipboardDiv.style.display = "none";
261                     showClipboard.innerHTML = "Show Clipboard";
262                     clipboardElement.onchange();
263                 }
264
265             };
266
267
268             // Show/Hide keyboard
269             var keyboardContainer = document.getElementById("keyboardContainer");
270             var showKeyboard = document.getElementById("showKeyboard");
271             showKeyboard.onclick = function() {
272
273                 var displayed = keyboardContainer.style.display;
274                 if (displayed != "block") {
275                     keyboardContainer.style.display = "block";
276                     showKeyboard.textContent = "Hide Keyboard";
277                 }
278                 else {
279                     keyboardContainer.style.display = "none";
280                     showKeyboard.textContent = "Show Keyboard";
281                 }
282
283             };
284
285             // On-screen keyboard
286             var osKeyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty.xml");
287             keyboardContainer.appendChild(osKeyboard);
288
289             osKeyboard.setKeyPressedHandler(
290                     function(keysym) {
291                         guac.sendKeyEvent(1, keysym);
292                     }
293             );
294
295             osKeyboard.setKeyReleasedHandler(
296                     function(keysym) {
297                         guac.sendKeyEvent(0, keysym);
298                     }
299             );
300
301             // Send Ctrl-Alt-Delete
302             var ctrlAltDelete = document.getElementById("ctrlAltDelete");
303
304             ctrlAltDelete.onclick = function() {
305
306                 var KEYSYM_CTRL   = 0xFF03;
307                 var KEYSYM_ALT    = 0xFFE9;
308                 var KEYSYM_DELETE = 0xFFFF;
309
310                 guac.sendKeyEvent(1, KEYSYM_CTRL);
311                 guac.sendKeyEvent(1, KEYSYM_ALT);
312                 guac.sendKeyEvent(1, KEYSYM_DELETE);
313                 guac.sendKeyEvent(0, KEYSYM_DELETE);
314                 guac.sendKeyEvent(0, KEYSYM_ALT);
315                 guac.sendKeyEvent(0, KEYSYM_CTRL);
316             };
317
318             // Logout
319             var logout = document.getElementById("logout");
320
321             logout.onclick = function() {
322                 window.location.href = "logout";
323             };
324
325             try {
326
327                 // Get ID
328                 var url = window.location.href;
329                 var query = url.indexOf("?");
330                 var id = url.substring(query+1);
331
332                 // Connect client
333                 guac.connect("id=" + id);
334
335             }
336             catch (e) {
337                 // TODO: Handle exception ...
338             }
339
340
341             /* ]]> */ </script>
342
343     </body>
344
345 </html>