]>
Commit | Line | Data |
---|---|---|
61dd52c9 | 1 | /* |
15046f00 | 2 | * noVNC: HTML5 VNC client |
d58f8b51 | 3 | * Copyright (C) 2012 Joel Martin |
1d728ace | 4 | * Licensed under MPL 2.0 (see LICENSE.txt) |
15046f00 JM |
5 | * |
6 | * See README.md for usage and integration instructions. | |
61dd52c9 JM |
7 | */ |
8 | ||
15046f00 JM |
9 | "use strict"; |
10 | /*jslint bitwise: false, white: false */ | |
a59f1cd2 | 11 | /*global window, console, document, navigator, ActiveXObject */ |
61dd52c9 | 12 | |
15046f00 | 13 | // Globals defined here |
a59f1cd2 | 14 | var Util = {}; |
15046f00 | 15 | |
81e5adaf | 16 | |
56ec48be JM |
17 | /* |
18 | * Make arrays quack | |
19 | */ | |
20 | ||
56ec48be JM |
21 | Array.prototype.push8 = function (num) { |
22 | this.push(num & 0xFF); | |
23 | }; | |
24 | ||
56ec48be JM |
25 | Array.prototype.push16 = function (num) { |
26 | this.push((num >> 8) & 0xFF, | |
27 | (num ) & 0xFF ); | |
28 | }; | |
56ec48be JM |
29 | Array.prototype.push32 = function (num) { |
30 | this.push((num >> 24) & 0xFF, | |
31 | (num >> 16) & 0xFF, | |
32 | (num >> 8) & 0xFF, | |
33 | (num ) & 0xFF ); | |
34 | }; | |
56ec48be | 35 | |
32f135d7 JM |
36 | // IE does not support map (even in IE9) |
37 | //This prototype is provided by the Mozilla foundation and | |
38 | //is distributed under the MIT license. | |
39 | //http://www.ibiblio.org/pub/Linux/LICENSES/mit.license | |
40 | if (!Array.prototype.map) | |
41 | { | |
42 | Array.prototype.map = function(fun /*, thisp*/) | |
43 | { | |
44 | var len = this.length; | |
45 | if (typeof fun != "function") | |
46 | throw new TypeError(); | |
47 | ||
48 | var res = new Array(len); | |
49 | var thisp = arguments[1]; | |
50 | for (var i = 0; i < len; i++) | |
51 | { | |
52 | if (i in this) | |
53 | res[i] = fun.call(thisp, this[i], i, this); | |
54 | } | |
55 | ||
56 | return res; | |
57 | }; | |
58 | } | |
59 | ||
34d8b844 JM |
60 | // |
61 | // requestAnimationFrame shim with setTimeout fallback | |
62 | // | |
63 | ||
64 | window.requestAnimFrame = (function(){ | |
65 | return window.requestAnimationFrame || | |
66 | window.webkitRequestAnimationFrame || | |
67 | window.mozRequestAnimationFrame || | |
68 | window.oRequestAnimationFrame || | |
69 | window.msRequestAnimationFrame || | |
70 | function(callback){ | |
71 | window.setTimeout(callback, 1000 / 60); | |
72 | }; | |
73 | })(); | |
74 | ||
15046f00 JM |
75 | /* |
76 | * ------------------------------------------------------ | |
77 | * Namespaced in Util | |
78 | * ------------------------------------------------------ | |
79 | */ | |
80 | ||
8db09746 JM |
81 | /* |
82 | * Logging/debug routines | |
83 | */ | |
84 | ||
c1eba48f | 85 | Util._log_level = 'warn'; |
8db09746 | 86 | Util.init_logging = function (level) { |
c1eba48f | 87 | if (typeof level === 'undefined') { |
c1eba48f JM |
88 | level = Util._log_level; |
89 | } else { | |
90 | Util._log_level = level; | |
91 | } | |
8db09746 JM |
92 | if (typeof window.console === "undefined") { |
93 | if (typeof window.opera !== "undefined") { | |
94 | window.console = { | |
95 | 'log' : window.opera.postError, | |
96 | 'warn' : window.opera.postError, | |
97 | 'error': window.opera.postError }; | |
98 | } else { | |
99 | window.console = { | |
100 | 'log' : function(m) {}, | |
101 | 'warn' : function(m) {}, | |
102 | 'error': function(m) {}}; | |
103 | } | |
104 | } | |
105 | ||
106 | Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {}; | |
107 | switch (level) { | |
108 | case 'debug': Util.Debug = function (msg) { console.log(msg); }; | |
109 | case 'info': Util.Info = function (msg) { console.log(msg); }; | |
110 | case 'warn': Util.Warn = function (msg) { console.warn(msg); }; | |
111 | case 'error': Util.Error = function (msg) { console.error(msg); }; | |
112 | case 'none': | |
113 | break; | |
114 | default: | |
115 | throw("invalid logging type '" + level + "'"); | |
116 | } | |
117 | }; | |
c1eba48f | 118 | Util.get_logging = function () { |
8d5d2c82 | 119 | return Util._log_level; |
d3796c14 | 120 | }; |
8db09746 | 121 | // Initialize logging level |
c1eba48f | 122 | Util.init_logging(); |
8db09746 | 123 | |
a8edf9d8 | 124 | |
5210330a JM |
125 | // Set configuration default for Crockford style function namespaces |
126 | Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) { | |
127 | var getter, setter; | |
128 | ||
129 | // Default getter function | |
130 | getter = function (idx) { | |
131 | if ((type in {'arr':1, 'array':1}) && | |
132 | (typeof idx !== 'undefined')) { | |
133 | return cfg[v][idx]; | |
134 | } else { | |
135 | return cfg[v]; | |
136 | } | |
137 | }; | |
138 | ||
139 | // Default setter function | |
140 | setter = function (val, idx) { | |
141 | if (type in {'boolean':1, 'bool':1}) { | |
142 | if ((!val) || (val in {'0':1, 'no':1, 'false':1})) { | |
143 | val = false; | |
d890e864 | 144 | } else { |
5210330a | 145 | val = true; |
d890e864 | 146 | } |
5210330a JM |
147 | } else if (type in {'integer':1, 'int':1}) { |
148 | val = parseInt(val, 10); | |
b50f3406 | 149 | } else if (type === 'str') { |
12acb663 | 150 | val = String(val); |
5210330a JM |
151 | } else if (type === 'func') { |
152 | if (!val) { | |
153 | val = function () {}; | |
154 | } | |
155 | } | |
156 | if (typeof idx !== 'undefined') { | |
157 | cfg[v][idx] = val; | |
158 | } else { | |
159 | cfg[v] = val; | |
160 | } | |
161 | }; | |
162 | ||
163 | // Set the description | |
164 | api[v + '_description'] = desc; | |
165 | ||
166 | // Set the getter function | |
167 | if (typeof api['get_' + v] === 'undefined') { | |
168 | api['get_' + v] = getter; | |
125d8bbb | 169 | } |
d890e864 | 170 | |
5210330a | 171 | // Set the setter function with extra sanity checks |
125d8bbb | 172 | if (typeof api['set_' + v] === 'undefined') { |
d890e864 | 173 | api['set_' + v] = function (val, idx) { |
5210330a JM |
174 | if (mode in {'RO':1, 'ro':1}) { |
175 | throw(v + " is read-only"); | |
176 | } else if ((mode in {'WO':1, 'wo':1}) && | |
177 | (typeof cfg[v] !== 'undefined')) { | |
178 | throw(v + " can only be set once"); | |
d890e864 | 179 | } |
5210330a | 180 | setter(val, idx); |
d890e864 | 181 | }; |
125d8bbb | 182 | } |
ff36b127 | 183 | |
5210330a JM |
184 | // Set the default value |
185 | if (typeof defaults[v] !== 'undefined') { | |
186 | defval = defaults[v]; | |
187 | } else if ((type in {'arr':1, 'array':1}) && | |
188 | (! (defval instanceof Array))) { | |
189 | defval = []; | |
ff36b127 | 190 | } |
5210330a JM |
191 | // Coerce existing setting to the right type |
192 | //Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]); | |
193 | setter(defval); | |
125d8bbb JM |
194 | }; |
195 | ||
5210330a JM |
196 | // Set group of configuration defaults |
197 | Util.conf_defaults = function(cfg, api, defaults, arr) { | |
198 | var i; | |
199 | for (i = 0; i < arr.length; i++) { | |
200 | Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1], | |
201 | arr[i][2], arr[i][3], arr[i][4]); | |
202 | } | |
ff4bfcb7 | 203 | }; |
125d8bbb | 204 | |
a8edf9d8 | 205 | |
15046f00 JM |
206 | /* |
207 | * Cross-browser routines | |
208 | */ | |
209 | ||
6f4b1e40 JM |
210 | |
211 | // Dynamically load scripts without using document.write() | |
212 | // Reference: http://unixpapa.com/js/dyna.html | |
213 | // | |
214 | // Handles the case where load_scripts is invoked from a script that | |
215 | // itself is loaded via load_scripts. Once all scripts are loaded the | |
216 | // window.onscriptsloaded handler is called (if set). | |
217 | Util.get_include_uri = function() { | |
218 | return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/"; | |
219 | } | |
220 | Util._pending_scripts = []; | |
221 | Util.load_scripts = function(files) { | |
222 | var head = document.getElementsByTagName('head')[0], | |
223 | ps = Util._pending_scripts; | |
224 | for (var f=0; f<files.length; f++) { | |
225 | var script = document.createElement('script'); | |
226 | script.type = 'text/javascript'; | |
227 | script.src = Util.get_include_uri() + files[f]; | |
228 | //console.log("loading script: " + Util.get_include_uri() + files[f]); | |
229 | head.appendChild(script); | |
230 | ps.push(script); | |
231 | script.onload = script.onreadystatechange = function (e) { | |
232 | if (!this.readyState || | |
233 | this.readyState == 'complete' || | |
234 | this.readyState == 'loaded') { | |
235 | this.onload = this.onreadystatechange = null; | |
236 | if (ps.indexOf(this) >= 0) { | |
237 | //console.log("loaded script: " + this.src); | |
238 | ps.splice(ps.indexOf(this), 1); | |
239 | } | |
240 | // Call window.onscriptsload after last script loads | |
241 | if (ps.length === 0 && window.onscriptsload) { | |
242 | window.onscriptsload(); | |
243 | } | |
244 | } | |
245 | } | |
246 | } | |
247 | } | |
248 | ||
15046f00 JM |
249 | // Get DOM element position on page |
250 | Util.getPosition = function (obj) { | |
251 | var x = 0, y = 0; | |
252 | if (obj.offsetParent) { | |
253 | do { | |
254 | x += obj.offsetLeft; | |
255 | y += obj.offsetTop; | |
256 | obj = obj.offsetParent; | |
257 | } while (obj); | |
258 | } | |
259 | return {'x': x, 'y': y}; | |
260 | }; | |
261 | ||
262 | // Get mouse event position in DOM element | |
125d8bbb | 263 | Util.getEventPosition = function (e, obj, scale) { |
15046f00 JM |
264 | var evt, docX, docY, pos; |
265 | //if (!e) evt = window.event; | |
266 | evt = (e ? e : window.event); | |
ad3f7624 | 267 | evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt); |
15046f00 JM |
268 | if (evt.pageX || evt.pageY) { |
269 | docX = evt.pageX; | |
270 | docY = evt.pageY; | |
271 | } else if (evt.clientX || evt.clientY) { | |
272 | docX = evt.clientX + document.body.scrollLeft + | |
273 | document.documentElement.scrollLeft; | |
274 | docY = evt.clientY + document.body.scrollTop + | |
275 | document.documentElement.scrollTop; | |
276 | } | |
277 | pos = Util.getPosition(obj); | |
125d8bbb JM |
278 | if (typeof scale === "undefined") { |
279 | scale = 1; | |
280 | } | |
281 | return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale}; | |
15046f00 JM |
282 | }; |
283 | ||
284 | ||
285 | // Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events | |
286 | Util.addEvent = function (obj, evType, fn){ | |
d93d3e09 | 287 | if (obj.attachEvent){ |
15046f00 JM |
288 | var r = obj.attachEvent("on"+evType, fn); |
289 | return r; | |
d93d3e09 JM |
290 | } else if (obj.addEventListener){ |
291 | obj.addEventListener(evType, fn, false); | |
292 | return true; | |
15046f00 JM |
293 | } else { |
294 | throw("Handler could not be attached"); | |
295 | } | |
296 | }; | |
297 | ||
298 | Util.removeEvent = function(obj, evType, fn){ | |
d93d3e09 | 299 | if (obj.detachEvent){ |
15046f00 JM |
300 | var r = obj.detachEvent("on"+evType, fn); |
301 | return r; | |
d93d3e09 JM |
302 | } else if (obj.removeEventListener){ |
303 | obj.removeEventListener(evType, fn, false); | |
304 | return true; | |
15046f00 JM |
305 | } else { |
306 | throw("Handler could not be removed"); | |
307 | } | |
308 | }; | |
309 | ||
310 | Util.stopEvent = function(e) { | |
311 | if (e.stopPropagation) { e.stopPropagation(); } | |
312 | else { e.cancelBubble = true; } | |
313 | ||
314 | if (e.preventDefault) { e.preventDefault(); } | |
315 | else { e.returnValue = false; } | |
316 | }; | |
317 | ||
318 | ||
319 | // Set browser engine versions. Based on mootools. | |
320 | Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}; | |
321 | ||
322 | Util.Engine = { | |
aa670567 JM |
323 | // Version detection break in Opera 11.60 (errors on arguments.callee.caller reference) |
324 | //'presto': (function() { | |
c3a172b9 | 325 | // return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()), |
aa670567 JM |
326 | 'presto': (function() { return (!window.opera) ? false : true; }()), |
327 | ||
15046f00 JM |
328 | 'trident': (function() { |
329 | return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()), | |
330 | 'webkit': (function() { | |
11bb7a4a JM |
331 | try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()), |
332 | //'webkit': (function() { | |
333 | // return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()), | |
15046f00 | 334 | 'gecko': (function() { |
8787e49b | 335 | return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }()) |
15046f00 | 336 | }; |
cdb55d26 JM |
337 | if (Util.Engine.webkit) { |
338 | // Extract actual webkit version if available | |
339 | Util.Engine.webkit = (function(v) { | |
340 | var re = new RegExp('WebKit/([0-9\.]*) '); | |
341 | v = (navigator.userAgent.match(re) || ['', v])[1]; | |
342 | return parseFloat(v, 10); | |
343 | })(Util.Engine.webkit); | |
344 | } | |
15046f00 JM |
345 | |
346 | Util.Flash = (function(){ | |
347 | var v, version; | |
348 | try { | |
349 | v = navigator.plugins['Shockwave Flash'].description; | |
350 | } catch(err1) { | |
351 | try { | |
352 | v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); | |
353 | } catch(err2) { | |
354 | v = '0 r0'; | |
355 | } | |
356 | } | |
357 | version = v.match(/\d+/g); | |
358 | return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; | |
a59f1cd2 | 359 | }()); |