]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * noVNC: HTML5 VNC client | |
3 | * Copyright (C) 2010 Joel Martin | |
4 | * Licensed under LGPL-3 (see LICENSE.txt) | |
5 | * | |
6 | * See README.md for usage and integration instructions. | |
7 | */ | |
8 | "use strict"; | |
9 | /*jslint white: false */ | |
10 | /*global $, Util, RFB, Canvas, Element, Fx */ | |
11 | ||
12 | var UI = { | |
13 | ||
14 | settingsOpen : false, | |
15 | ||
16 | // Render default UI and initialize settings menu | |
17 | load: function(target) { | |
18 | var html = '', i, sheet, sheets, llevels; | |
19 | ||
20 | /* Populate the 'target' DOM element with default UI */ | |
21 | if (!target) { | |
22 | target = $D('vnc'); | |
23 | } else if (typeof target === 'string') { | |
24 | target = $D(target); | |
25 | } | |
26 | ||
27 | if ((!document.createElement('canvas').getContext) && | |
28 | window.ActiveXObject) { | |
29 | // Suggest Chrome frame for Internet Explorer users | |
30 | html += '<center><div style="text-align: left; width: 400px">'; | |
31 | html += ' You are using a version of Internet Explorer '; | |
32 | html += ' that does not have HTML5 Canvas support. '; | |
33 | html += ' To use noVNC you must use a browser with HTML5 '; | |
34 | html += ' Canvas support or install '; | |
35 | html += ' <a href="http://google.com/chromeframe" target="cframe">'; | |
36 | html += ' Google Chrome Frame.</a>'; | |
37 | html += '</div></center>'; | |
38 | target.innerHTML = html; | |
39 | return; | |
40 | } | |
41 | ||
42 | html += '<div id="VNC_controls">'; | |
43 | html += ' <ul>'; | |
44 | html += ' <li>Host: <input id="VNC_host"></li>'; | |
45 | html += ' <li>Port: <input id="VNC_port"></li>'; | |
46 | html += ' <li>Password: <input id="VNC_password"'; | |
47 | html += ' type="password"></li>'; | |
48 | html += ' <li><input id="VNC_connect_button" type="button"'; | |
49 | html += ' value="Loading" disabled></li>'; | |
50 | html += ' </ul>'; | |
51 | html += '</div>'; | |
52 | html += '<div id="VNC_screen">'; | |
53 | html += ' <div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">'; | |
54 | html += ' <table border=0 width=100%><tr>'; | |
55 | html += ' <td><div id="VNC_status">Loading</div></td>'; | |
56 | html += ' <td width=1%><div class="VNC_buttons_right">'; | |
57 | html += ' <input type=button class="VNC_status_button" value="Settings"'; | |
58 | html += ' id="menuButton"'; | |
59 | html += ' onclick="UI.clickSettingsMenu();">'; | |
60 | html += ' <span id="VNC_settings_menu"'; | |
61 | html += ' onmouseover="UI.canvasBlur();"'; | |
62 | html += ' onmouseout="UI.canvasFocus();">'; | |
63 | html += ' <ul>'; | |
64 | html += ' <li><input id="VNC_encrypt"'; | |
65 | html += ' type="checkbox"> Encrypt</li>'; | |
66 | html += ' <li><input id="VNC_true_color"'; | |
67 | html += ' type="checkbox" checked> True Color</li>'; | |
68 | html += ' <li><input id="VNC_cursor"'; | |
69 | html += ' type="checkbox"> Local Cursor</li>'; | |
70 | html += ' <li><input id="VNC_shared"'; | |
71 | html += ' type="checkbox"> Shared Mode</li>'; | |
72 | html += ' <li><input id="VNC_connectTimeout"'; | |
73 | html += ' type="input"> Connect Timeout (s)</li>'; | |
74 | html += ' <hr>'; | |
75 | ||
76 | // Stylesheet selection dropdown | |
77 | html += ' <li><select id="VNC_stylesheet" name="vncStyle">'; | |
78 | html += ' <option value="default">default</option>'; | |
79 | sheet = WebUtil.selectStylesheet(); | |
80 | sheets = WebUtil.getStylesheets(); | |
81 | for (i = 0; i < sheets.length; i += 1) { | |
82 | html += '<option value="' + sheets[i].title + '">' + sheets[i].title + '</option>'; | |
83 | } | |
84 | html += ' </select> Style</li>'; | |
85 | ||
86 | // Logging selection dropdown | |
87 | html += ' <li><select id="VNC_logging" name="vncLogging">'; | |
88 | llevels = ['error', 'warn', 'info', 'debug']; | |
89 | for (i = 0; i < llevels.length; i += 1) { | |
90 | html += '<option value="' + llevels[i] + '">' + llevels[i] + '</option>'; | |
91 | } | |
92 | html += ' </select> Logging</li>'; | |
93 | ||
94 | html += ' <hr>'; | |
95 | html += ' <li><input type="button" id="VNC_apply" value="Apply"'; | |
96 | html += ' onclick="UI.settingsApply()"></li>'; | |
97 | html += ' </ul>'; | |
98 | html += ' </span></div></td>'; | |
99 | html += ' <td width=1%><div class="VNC_buttons_right">'; | |
100 | html += ' <input type=button class="VNC_status_button" value="Send CtrlAltDel"'; | |
101 | html += ' id="sendCtrlAltDelButton"'; | |
102 | html += ' onclick="UI.sendCtrlAltDel();"></div></td>'; | |
103 | html += ' </tr></table>'; | |
104 | html += ' </div>'; | |
105 | html += ' <canvas id="VNC_canvas" width="640px" height="20px">'; | |
106 | html += ' Canvas not supported.'; | |
107 | html += ' </canvas>'; | |
108 | html += '</div>'; | |
109 | html += '<br><br>'; | |
110 | html += '<div id="VNC_clipboard">'; | |
111 | html += ' VNC Clipboard:'; | |
112 | html += ' <input id="VNC_clipboard_clear_button"'; | |
113 | html += ' type="button" value="Clear"'; | |
114 | html += ' onclick="UI.clipClear();">'; | |
115 | html += ' <br>'; | |
116 | html += ' <textarea id="VNC_clipboard_text" cols=80 rows=5'; | |
117 | html += ' onfocus="UI.canvasBlur();"'; | |
118 | html += ' onblur="UI.canvasFocus();"'; | |
119 | html += ' onchange="UI.clipSend();"></textarea>'; | |
120 | html += '</div>'; | |
121 | target.innerHTML = html; | |
122 | ||
123 | // Settings with immediate effects | |
124 | UI.initSetting('logging', 'warn'); | |
125 | WebUtil.init_logging(UI.getSetting('logging')); | |
126 | UI.initSetting('stylesheet', 'default'); | |
127 | ||
128 | WebUtil.selectStylesheet(null); // call twice to get around webkit bug | |
129 | WebUtil.selectStylesheet(UI.getSetting('stylesheet')); | |
130 | ||
131 | /* Populate the controls if defaults are provided in the URL */ | |
132 | UI.initSetting('host', ''); | |
133 | UI.initSetting('port', ''); | |
134 | UI.initSetting('password', ''); | |
135 | UI.initSetting('encrypt', false); | |
136 | UI.initSetting('true_color', true); | |
137 | UI.initSetting('cursor', false); | |
138 | UI.initSetting('shared', true); | |
139 | UI.initSetting('connectTimeout', 2); | |
140 | ||
141 | UI.rfb = RFB({'target': $D('VNC_canvas'), | |
142 | 'updateState': UI.updateState, | |
143 | 'clipboardReceive': UI.clipReceive}); | |
144 | ||
145 | // Unfocus clipboard when over the VNC area | |
146 | $D('VNC_screen').onmousemove = function () { | |
147 | var canvas = UI.rfb.get_canvas(); | |
148 | if ((! canvas) || (! canvas.get_focused())) { | |
149 | $D('VNC_clipboard_text').blur(); | |
150 | } | |
151 | }; | |
152 | ||
153 | }, | |
154 | ||
155 | // Read form control compatible setting from cookie | |
156 | getSetting: function(name) { | |
157 | var val, ctrl = $D('VNC_' + name); | |
158 | val = WebUtil.readCookie(name); | |
159 | if (ctrl.type === 'checkbox') { | |
160 | if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) { | |
161 | val = false; | |
162 | } else { | |
163 | val = true; | |
164 | } | |
165 | } | |
166 | return val; | |
167 | }, | |
168 | ||
169 | // Update cookie and form control setting. If value is not set, then | |
170 | // updates from control to current cookie setting. | |
171 | updateSetting: function(name, value) { | |
172 | var i, ctrl = $D('VNC_' + name); | |
173 | // Save the cookie for this session | |
174 | if (typeof value !== 'undefined') { | |
175 | WebUtil.createCookie(name, value); | |
176 | } | |
177 | ||
178 | // Update the settings control | |
179 | value = UI.getSetting(name); | |
180 | if (ctrl.type === 'checkbox') { | |
181 | ctrl.checked = value; | |
182 | } else if (typeof ctrl.options !== 'undefined') { | |
183 | for (i = 0; i < ctrl.options.length; i += 1) { | |
184 | if (ctrl.options[i].value === value) { | |
185 | ctrl.selectedIndex = i; | |
186 | break; | |
187 | } | |
188 | } | |
189 | } else { | |
190 | ctrl.value = value; | |
191 | } | |
192 | }, | |
193 | ||
194 | // Save control setting to cookie | |
195 | saveSetting: function(name) { | |
196 | var val, ctrl = $D('VNC_' + name); | |
197 | if (ctrl.type === 'checkbox') { | |
198 | val = ctrl.checked; | |
199 | } else if (typeof ctrl.options !== 'undefined') { | |
200 | val = ctrl.options[ctrl.selectedIndex].value; | |
201 | } else { | |
202 | val = ctrl.value; | |
203 | } | |
204 | WebUtil.createCookie(name, val); | |
205 | //Util.Debug("Setting saved '" + name + "=" + val + "'"); | |
206 | return val; | |
207 | }, | |
208 | ||
209 | // Initial page load read/initialization of settings | |
210 | initSetting: function(name, defVal) { | |
211 | var val; | |
212 | ||
213 | // Check Query string followed by cookie | |
214 | val = WebUtil.getQueryVar(name); | |
215 | if (val === null) { | |
216 | val = WebUtil.readCookie(name, defVal); | |
217 | } | |
218 | UI.updateSetting(name, val); | |
219 | //Util.Debug("Setting '" + name + "' initialized to '" + val + "'"); | |
220 | return val; | |
221 | }, | |
222 | ||
223 | ||
224 | // Toggle the settings menu: | |
225 | // On open, settings are refreshed from saved cookies. | |
226 | // On close, settings are applied | |
227 | clickSettingsMenu: function() { | |
228 | if (UI.settingsOpen) { | |
229 | UI.settingsApply(); | |
230 | ||
231 | UI.closeSettingsMenu(); | |
232 | } else { | |
233 | UI.updateSetting('encrypt'); | |
234 | UI.updateSetting('true_color'); | |
235 | if (UI.rfb.get_canvas().get_cursor_uri()) { | |
236 | UI.updateSetting('cursor'); | |
237 | } else { | |
238 | UI.updateSetting('cursor', false); | |
239 | $D('VNC_cursor').disabled = true; | |
240 | } | |
241 | UI.updateSetting('shared'); | |
242 | UI.updateSetting('connectTimeout'); | |
243 | UI.updateSetting('stylesheet'); | |
244 | UI.updateSetting('logging'); | |
245 | ||
246 | UI.openSettingsMenu(); | |
247 | } | |
248 | }, | |
249 | ||
250 | // Open menu | |
251 | openSettingsMenu: function() { | |
252 | $D('VNC_settings_menu').style.display = "block"; | |
253 | UI.settingsOpen = true; | |
254 | }, | |
255 | ||
256 | // Close menu (without applying settings) | |
257 | closeSettingsMenu: function() { | |
258 | $D('VNC_settings_menu').style.display = "none"; | |
259 | UI.settingsOpen = false; | |
260 | }, | |
261 | ||
262 | // Disable/enable controls depending on connection state | |
263 | settingsDisabled: function(disabled, rfb) { | |
264 | //Util.Debug(">> settingsDisabled"); | |
265 | $D('VNC_encrypt').disabled = disabled; | |
266 | $D('VNC_true_color').disabled = disabled; | |
267 | if (rfb && rfb.get_canvas() && rfb.get_canvas().get_cursor_uri()) { | |
268 | $D('VNC_cursor').disabled = disabled; | |
269 | } else { | |
270 | UI.updateSetting('cursor', false); | |
271 | $D('VNC_cursor').disabled = true; | |
272 | } | |
273 | $D('VNC_shared').disabled = disabled; | |
274 | $D('VNC_connectTimeout').disabled = disabled; | |
275 | //Util.Debug("<< settingsDisabled"); | |
276 | }, | |
277 | ||
278 | // Save/apply settings when 'Apply' button is pressed | |
279 | settingsApply: function() { | |
280 | //Util.Debug(">> settingsApply"); | |
281 | UI.saveSetting('encrypt'); | |
282 | UI.saveSetting('true_color'); | |
283 | if (UI.rfb.get_canvas().get_cursor_uri()) { | |
284 | UI.saveSetting('cursor'); | |
285 | } | |
286 | UI.saveSetting('shared'); | |
287 | UI.saveSetting('connectTimeout'); | |
288 | UI.saveSetting('stylesheet'); | |
289 | UI.saveSetting('logging'); | |
290 | ||
291 | // Settings with immediate (non-connected related) effect | |
292 | WebUtil.selectStylesheet(UI.getSetting('stylesheet')); | |
293 | WebUtil.init_logging(UI.getSetting('logging')); | |
294 | ||
295 | //Util.Debug("<< settingsApply"); | |
296 | }, | |
297 | ||
298 | ||
299 | ||
300 | setPassword: function() { | |
301 | UI.rfb.sendPassword($D('VNC_password').value); | |
302 | return false; | |
303 | }, | |
304 | ||
305 | sendCtrlAltDel: function() { | |
306 | UI.rfb.sendCtrlAltDel(); | |
307 | }, | |
308 | ||
309 | updateState: function(rfb, state, oldstate, msg) { | |
310 | var s, sb, c, cad, klass; | |
311 | s = $D('VNC_status'); | |
312 | sb = $D('VNC_status_bar'); | |
313 | c = $D('VNC_connect_button'); | |
314 | cad = $D('sendCtrlAltDelButton'); | |
315 | switch (state) { | |
316 | case 'failed': | |
317 | case 'fatal': | |
318 | c.disabled = true; | |
319 | cad.disabled = true; | |
320 | UI.settingsDisabled(true, rfb); | |
321 | klass = "VNC_status_error"; | |
322 | break; | |
323 | case 'normal': | |
324 | c.value = "Disconnect"; | |
325 | c.onclick = UI.disconnect; | |
326 | c.disabled = false; | |
327 | cad.disabled = false; | |
328 | UI.settingsDisabled(true, rfb); | |
329 | klass = "VNC_status_normal"; | |
330 | break; | |
331 | case 'disconnected': | |
332 | case 'loaded': | |
333 | c.value = "Connect"; | |
334 | c.onclick = UI.connect; | |
335 | ||
336 | c.disabled = false; | |
337 | cad.disabled = true; | |
338 | UI.settingsDisabled(false, rfb); | |
339 | klass = "VNC_status_normal"; | |
340 | break; | |
341 | case 'password': | |
342 | c.value = "Send Password"; | |
343 | c.onclick = UI.setPassword; | |
344 | ||
345 | c.disabled = false; | |
346 | cad.disabled = true; | |
347 | UI.settingsDisabled(true, rfb); | |
348 | klass = "VNC_status_warn"; | |
349 | break; | |
350 | default: | |
351 | c.disabled = true; | |
352 | cad.disabled = true; | |
353 | UI.settingsDisabled(true, rfb); | |
354 | klass = "VNC_status_warn"; | |
355 | break; | |
356 | } | |
357 | ||
358 | if (typeof(msg) !== 'undefined') { | |
359 | s.setAttribute("class", klass); | |
360 | sb.setAttribute("class", klass); | |
361 | s.innerHTML = msg; | |
362 | } | |
363 | ||
364 | }, | |
365 | ||
366 | clipReceive: function(rfb, text) { | |
367 | Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "..."); | |
368 | $D('VNC_clipboard_text').value = text; | |
369 | Util.Debug("<< UI.clipReceive"); | |
370 | }, | |
371 | ||
372 | ||
373 | connect: function() { | |
374 | var host, port, password; | |
375 | ||
376 | UI.closeSettingsMenu(); | |
377 | ||
378 | host = $D('VNC_host').value; | |
379 | port = $D('VNC_port').value; | |
380 | password = $D('VNC_password').value; | |
381 | if ((!host) || (!port)) { | |
382 | throw("Must set host and port"); | |
383 | } | |
384 | ||
385 | UI.rfb.set_encrypt(UI.getSetting('encrypt')); | |
386 | UI.rfb.set_true_color(UI.getSetting('true_color')); | |
387 | UI.rfb.set_local_cursor(UI.getSetting('cursor')); | |
388 | UI.rfb.set_shared(UI.getSetting('shared')); | |
389 | UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout')); | |
390 | ||
391 | UI.rfb.connect(host, port, password); | |
392 | }, | |
393 | ||
394 | disconnect: function() { | |
395 | UI.closeSettingsMenu(); | |
396 | ||
397 | UI.rfb.disconnect(); | |
398 | }, | |
399 | ||
400 | canvasBlur: function() { | |
401 | UI.rfb.get_canvas().set_focused(false); | |
402 | }, | |
403 | ||
404 | canvasFocus: function() { | |
405 | UI.rfb.get_canvas().set_focused(true); | |
406 | }, | |
407 | ||
408 | clipClear: function() { | |
409 | $D('VNC_clipboard_text').value = ""; | |
410 | UI.rfb.clipboardPasteFrom(""); | |
411 | }, | |
412 | ||
413 | clipSend: function() { | |
414 | var text = $D('VNC_clipboard_text').value; | |
415 | Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "..."); | |
416 | UI.rfb.clipboardPasteFrom(text); | |
417 | Util.Debug("<< UI.clipSend"); | |
418 | } | |
419 | ||
420 | }; |