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