]>
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 | ||
d21cd6c1 SR |
9 | /* jshint white: false, nonstandard: true */ |
10 | /*global window, console, document, navigator, ActiveXObject, INCLUDE_URI */ | |
61dd52c9 | 11 | |
15046f00 | 12 | // Globals defined here |
a59f1cd2 | 13 | var Util = {}; |
15046f00 | 14 | |
81e5adaf | 15 | |
56ec48be JM |
16 | /* |
17 | * Make arrays quack | |
18 | */ | |
19 | ||
f9fd0313 SR |
20 | var addFunc = function (cl, name, func) { |
21 | if (!cl.prototype[name]) { | |
22 | Object.defineProperty(cl.prototype, name, { enumerable: false, value: func }); | |
23 | } | |
24 | }; | |
25 | ||
26 | addFunc(Array, 'push8', function (num) { | |
d21cd6c1 | 27 | "use strict"; |
56ec48be | 28 | this.push(num & 0xFF); |
f9fd0313 | 29 | }); |
56ec48be | 30 | |
f9fd0313 | 31 | addFunc(Array, 'push16', function (num) { |
d21cd6c1 | 32 | "use strict"; |
56ec48be | 33 | this.push((num >> 8) & 0xFF, |
d21cd6c1 | 34 | num & 0xFF); |
f9fd0313 SR |
35 | }); |
36 | ||
37 | addFunc(Array, 'push32', function (num) { | |
d21cd6c1 | 38 | "use strict"; |
56ec48be JM |
39 | this.push((num >> 24) & 0xFF, |
40 | (num >> 16) & 0xFF, | |
41 | (num >> 8) & 0xFF, | |
d21cd6c1 | 42 | num & 0xFF); |
f9fd0313 | 43 | }); |
56ec48be | 44 | |
32f135d7 JM |
45 | // IE does not support map (even in IE9) |
46 | //This prototype is provided by the Mozilla foundation and | |
47 | //is distributed under the MIT license. | |
48 | //http://www.ibiblio.org/pub/Linux/LICENSES/mit.license | |
f9fd0313 SR |
49 | addFunc(Array, 'map', function (fun /*, thisp*/) { |
50 | "use strict"; | |
51 | var len = this.length; | |
52 | if (typeof fun != "function") { | |
53 | throw new TypeError(); | |
54 | } | |
55 | ||
56 | var res = new Array(len); | |
57 | var thisp = arguments[1]; | |
58 | for (var i = 0; i < len; i++) { | |
59 | if (i in this) { | |
60 | res[i] = fun.call(thisp, this[i], i, this); | |
d21cd6c1 | 61 | } |
f9fd0313 | 62 | } |
d21cd6c1 | 63 | |
f9fd0313 SR |
64 | return res; |
65 | }); | |
32f135d7 | 66 | |
0fe30338 | 67 | // IE <9 does not support indexOf |
68 | //This prototype is provided by the Mozilla foundation and | |
69 | //is distributed under the MIT license. | |
70 | //http://www.ibiblio.org/pub/Linux/LICENSES/mit.license | |
f9fd0313 SR |
71 | addFunc(Array, 'indexOf', function (elt /*, from*/) { |
72 | "use strict"; | |
73 | var len = this.length >>> 0; | |
74 | ||
75 | var from = Number(arguments[1]) || 0; | |
76 | from = (from < 0) ? Math.ceil(from) : Math.floor(from); | |
77 | if (from < 0) { | |
78 | from += len; | |
79 | } | |
80 | ||
81 | for (; from < len; from++) { | |
82 | if (from in this && | |
83 | this[from] === elt) { | |
84 | return from; | |
d21cd6c1 | 85 | } |
f9fd0313 SR |
86 | } |
87 | return -1; | |
88 | }); | |
0fe30338 | 89 | |
95eb681b SR |
90 | // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys |
91 | if (!Object.keys) { | |
d21cd6c1 SR |
92 | Object.keys = (function () { |
93 | 'use strict'; | |
94 | var hasOwnProperty = Object.prototype.hasOwnProperty, | |
95 | hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), | |
96 | dontEnums = [ | |
97 | 'toString', | |
98 | 'toLocaleString', | |
99 | 'valueOf', | |
100 | 'hasOwnProperty', | |
101 | 'isPrototypeOf', | |
102 | 'propertyIsEnumerable', | |
103 | 'constructor' | |
104 | ], | |
105 | dontEnumsLength = dontEnums.length; | |
106 | ||
107 | return function (obj) { | |
108 | if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { | |
109 | throw new TypeError('Object.keys called on non-object'); | |
110 | } | |
95eb681b | 111 | |
d21cd6c1 | 112 | var result = [], prop, i; |
95eb681b | 113 | |
d21cd6c1 SR |
114 | for (prop in obj) { |
115 | if (hasOwnProperty.call(obj, prop)) { | |
116 | result.push(prop); | |
117 | } | |
118 | } | |
95eb681b | 119 | |
d21cd6c1 SR |
120 | if (hasDontEnumBug) { |
121 | for (i = 0; i < dontEnumsLength; i++) { | |
122 | if (hasOwnProperty.call(obj, dontEnums[i])) { | |
123 | result.push(dontEnums[i]); | |
124 | } | |
125 | } | |
126 | } | |
127 | return result; | |
128 | }; | |
129 | })(); | |
130 | } | |
95eb681b | 131 | |
d21cd6c1 SR |
132 | // PhantomJS 1.x doesn't support bind, |
133 | // so leave this in until PhantomJS 2.0 is released | |
134 | //This prototype is provided by the Mozilla foundation and | |
135 | //is distributed under the MIT license. | |
136 | //http://www.ibiblio.org/pub/Linux/LICENSES/mit.license | |
f9fd0313 SR |
137 | addFunc(Function, 'bind', function (oThis) { |
138 | if (typeof this !== "function") { | |
139 | // closest thing possible to the ECMAScript 5 | |
140 | // internal IsCallable function | |
141 | throw new TypeError("Function.prototype.bind - " + | |
142 | "what is trying to be bound is not callable"); | |
143 | } | |
144 | ||
145 | var aArgs = Array.prototype.slice.call(arguments, 1), | |
146 | fToBind = this, | |
147 | fNOP = function () {}, | |
148 | fBound = function () { | |
149 | return fToBind.apply(this instanceof fNOP && oThis ? this | |
150 | : oThis, | |
151 | aArgs.concat(Array.prototype.slice.call(arguments))); | |
152 | }; | |
d21cd6c1 | 153 | |
f9fd0313 SR |
154 | fNOP.prototype = this.prototype; |
155 | fBound.prototype = new fNOP(); | |
d21cd6c1 | 156 | |
f9fd0313 SR |
157 | return fBound; |
158 | }); | |
0fe30338 | 159 | |
d21cd6c1 | 160 | // |
34d8b844 JM |
161 | // requestAnimationFrame shim with setTimeout fallback |
162 | // | |
163 | ||
d21cd6c1 SR |
164 | window.requestAnimFrame = (function () { |
165 | "use strict"; | |
166 | return window.requestAnimationFrame || | |
167 | window.webkitRequestAnimationFrame || | |
168 | window.mozRequestAnimationFrame || | |
169 | window.oRequestAnimationFrame || | |
170 | window.msRequestAnimationFrame || | |
171 | function (callback) { | |
34d8b844 JM |
172 | window.setTimeout(callback, 1000 / 60); |
173 | }; | |
174 | })(); | |
175 | ||
d21cd6c1 | 176 | /* |
15046f00 JM |
177 | * ------------------------------------------------------ |
178 | * Namespaced in Util | |
179 | * ------------------------------------------------------ | |
180 | */ | |
181 | ||
8db09746 JM |
182 | /* |
183 | * Logging/debug routines | |
184 | */ | |
185 | ||
c1eba48f | 186 | Util._log_level = 'warn'; |
8db09746 | 187 | Util.init_logging = function (level) { |
d21cd6c1 | 188 | "use strict"; |
c1eba48f | 189 | if (typeof level === 'undefined') { |
c1eba48f JM |
190 | level = Util._log_level; |
191 | } else { | |
192 | Util._log_level = level; | |
193 | } | |
8db09746 JM |
194 | if (typeof window.console === "undefined") { |
195 | if (typeof window.opera !== "undefined") { | |
196 | window.console = { | |
197 | 'log' : window.opera.postError, | |
198 | 'warn' : window.opera.postError, | |
d21cd6c1 SR |
199 | 'error': window.opera.postError |
200 | }; | |
8db09746 JM |
201 | } else { |
202 | window.console = { | |
d21cd6c1 SR |
203 | 'log' : function (m) {}, |
204 | 'warn' : function (m) {}, | |
205 | 'error': function (m) {} | |
206 | }; | |
8db09746 JM |
207 | } |
208 | } | |
209 | ||
210 | Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {}; | |
d21cd6c1 | 211 | /* jshint -W086 */ |
8db09746 | 212 | switch (level) { |
d21cd6c1 SR |
213 | case 'debug': |
214 | Util.Debug = function (msg) { console.log(msg); }; | |
215 | case 'info': | |
216 | Util.Info = function (msg) { console.log(msg); }; | |
217 | case 'warn': | |
218 | Util.Warn = function (msg) { console.warn(msg); }; | |
219 | case 'error': | |
220 | Util.Error = function (msg) { console.error(msg); }; | |
8db09746 JM |
221 | case 'none': |
222 | break; | |
223 | default: | |
d21cd6c1 | 224 | throw new Error("invalid logging type '" + level + "'"); |
8db09746 | 225 | } |
d21cd6c1 | 226 | /* jshint +W086 */ |
8db09746 | 227 | }; |
c1eba48f | 228 | Util.get_logging = function () { |
8d5d2c82 | 229 | return Util._log_level; |
d3796c14 | 230 | }; |
8db09746 | 231 | // Initialize logging level |
c1eba48f | 232 | Util.init_logging(); |
8db09746 | 233 | |
d21cd6c1 SR |
234 | Util.make_property = function (proto, name, mode, type) { |
235 | "use strict"; | |
a8edf9d8 | 236 | |
d21cd6c1 SR |
237 | var getter; |
238 | if (type === 'arr') { | |
239 | getter = function (idx) { | |
240 | if (typeof idx !== 'undefined') { | |
241 | return this['_' + name][idx]; | |
242 | } else { | |
243 | return this['_' + name]; | |
244 | } | |
245 | }; | |
246 | } else { | |
247 | getter = function () { | |
248 | return this['_' + name]; | |
249 | }; | |
250 | } | |
5210330a | 251 | |
d21cd6c1 SR |
252 | var make_setter = function (process_val) { |
253 | if (process_val) { | |
254 | return function (val, idx) { | |
255 | if (typeof idx !== 'undefined') { | |
256 | this['_' + name][idx] = process_val(val); | |
257 | } else { | |
258 | this['_' + name] = process_val(val); | |
259 | } | |
260 | }; | |
5210330a | 261 | } else { |
d21cd6c1 SR |
262 | return function (val, idx) { |
263 | if (typeof idx !== 'undefined') { | |
264 | this['_' + name][idx] = val; | |
265 | } else { | |
266 | this['_' + name] = val; | |
267 | } | |
268 | }; | |
5210330a JM |
269 | } |
270 | }; | |
271 | ||
d21cd6c1 SR |
272 | var setter; |
273 | if (type === 'bool') { | |
274 | setter = make_setter(function (val) { | |
275 | if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) { | |
276 | return false; | |
d890e864 | 277 | } else { |
d21cd6c1 | 278 | return true; |
d890e864 | 279 | } |
d21cd6c1 SR |
280 | }); |
281 | } else if (type === 'int') { | |
282 | setter = make_setter(function (val) { return parseInt(val, 10); }); | |
283 | } else if (type === 'float') { | |
284 | setter = make_setter(parseFloat); | |
285 | } else if (type === 'str') { | |
286 | setter = make_setter(String); | |
287 | } else if (type === 'func') { | |
288 | setter = make_setter(function (val) { | |
5210330a | 289 | if (!val) { |
d21cd6c1 SR |
290 | return function () {}; |
291 | } else { | |
292 | return val; | |
5210330a | 293 | } |
d21cd6c1 SR |
294 | }); |
295 | } else if (type === 'arr' || type === 'dom' || type == 'raw') { | |
296 | setter = make_setter(); | |
297 | } else { | |
298 | throw new Error('Unknown property type ' + type); // some sanity checking | |
299 | } | |
5210330a | 300 | |
d21cd6c1 SR |
301 | // set the getter |
302 | if (typeof proto['get_' + name] === 'undefined') { | |
303 | proto['get_' + name] = getter; | |
125d8bbb | 304 | } |
d890e864 | 305 | |
d21cd6c1 SR |
306 | // set the setter if needed |
307 | if (typeof proto['set_' + name] === 'undefined') { | |
308 | if (mode === 'rw') { | |
309 | proto['set_' + name] = setter; | |
310 | } else if (mode === 'wo') { | |
311 | proto['set_' + name] = function (val, idx) { | |
312 | if (typeof this['_' + name] !== 'undefined') { | |
313 | throw new Error(name + " can only be set once"); | |
314 | } | |
315 | setter.call(this, val, idx); | |
316 | }; | |
317 | } | |
125d8bbb | 318 | } |
ff36b127 | 319 | |
d21cd6c1 SR |
320 | // make a special setter that we can use in set defaults |
321 | proto['_raw_set_' + name] = function (val, idx) { | |
322 | setter.call(this, val, idx); | |
323 | //delete this['_init_set_' + name]; // remove it after use | |
324 | }; | |
325 | }; | |
326 | ||
327 | Util.make_properties = function (constructor, arr) { | |
328 | "use strict"; | |
329 | for (var i = 0; i < arr.length; i++) { | |
330 | Util.make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]); | |
ff36b127 | 331 | } |
125d8bbb JM |
332 | }; |
333 | ||
d21cd6c1 SR |
334 | Util.set_defaults = function (obj, conf, defaults) { |
335 | var defaults_keys = Object.keys(defaults); | |
336 | var conf_keys = Object.keys(conf); | |
337 | var keys_obj = {}; | |
5210330a | 338 | var i; |
d21cd6c1 SR |
339 | for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; } |
340 | for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; } | |
341 | var keys = Object.keys(keys_obj); | |
342 | ||
343 | for (i = 0; i < keys.length; i++) { | |
344 | var setter = obj['_raw_set_' + keys[i]]; | |
7caa9c20 MA |
345 | if (!setter) { |
346 | Util.Warn('Invalid property ' + keys[i]); | |
347 | continue; | |
348 | } | |
d21cd6c1 | 349 | |
cfc02e5e | 350 | if (keys[i] in conf) { |
d21cd6c1 SR |
351 | setter.call(obj, conf[keys[i]]); |
352 | } else { | |
353 | setter.call(obj, defaults[keys[i]]); | |
354 | } | |
5210330a | 355 | } |
ff4bfcb7 | 356 | }; |
125d8bbb | 357 | |
b7996b04 | 358 | /* |
359 | * Decode from UTF-8 | |
360 | */ | |
d21cd6c1 SR |
361 | Util.decodeUTF8 = function (utf8string) { |
362 | "use strict"; | |
b7996b04 | 363 | return decodeURIComponent(escape(utf8string)); |
d21cd6c1 | 364 | }; |
b7996b04 | 365 | |
366 | ||
a8edf9d8 | 367 | |
15046f00 JM |
368 | /* |
369 | * Cross-browser routines | |
370 | */ | |
371 | ||
6f4b1e40 JM |
372 | |
373 | // Dynamically load scripts without using document.write() | |
374 | // Reference: http://unixpapa.com/js/dyna.html | |
375 | // | |
376 | // Handles the case where load_scripts is invoked from a script that | |
377 | // itself is loaded via load_scripts. Once all scripts are loaded the | |
378 | // window.onscriptsloaded handler is called (if set). | |
d21cd6c1 | 379 | Util.get_include_uri = function () { |
6f4b1e40 | 380 | return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/"; |
d21cd6c1 | 381 | }; |
73ee4fa7 | 382 | Util._loading_scripts = []; |
6f4b1e40 | 383 | Util._pending_scripts = []; |
d21cd6c1 SR |
384 | Util.load_scripts = function (files) { |
385 | "use strict"; | |
73ee4fa7 JM |
386 | var head = document.getElementsByTagName('head')[0], script, |
387 | ls = Util._loading_scripts, ps = Util._pending_scripts; | |
d21cd6c1 SR |
388 | |
389 | var loadFunc = function (e) { | |
390 | while (ls.length > 0 && (ls[0].readyState === 'loaded' || | |
391 | ls[0].readyState === 'complete')) { | |
392 | // For IE, append the script to trigger execution | |
393 | var s = ls.shift(); | |
394 | //console.log("loaded script: " + s.src); | |
395 | head.appendChild(s); | |
396 | } | |
397 | if (!this.readyState || | |
398 | (Util.Engine.presto && this.readyState === 'loaded') || | |
399 | this.readyState === 'complete') { | |
400 | if (ps.indexOf(this) >= 0) { | |
401 | this.onload = this.onreadystatechange = null; | |
402 | //console.log("completed script: " + this.src); | |
403 | ps.splice(ps.indexOf(this), 1); | |
404 | ||
405 | // Call window.onscriptsload after last script loads | |
406 | if (ps.length === 0 && window.onscriptsload) { | |
407 | window.onscriptsload(); | |
408 | } | |
409 | } | |
410 | } | |
411 | }; | |
412 | ||
413 | for (var f = 0; f < files.length; f++) { | |
73ee4fa7 JM |
414 | script = document.createElement('script'); |
415 | script.type = 'text/javascript'; | |
416 | script.src = Util.get_include_uri() + files[f]; | |
417 | //console.log("loading script: " + script.src); | |
d21cd6c1 | 418 | script.onload = script.onreadystatechange = loadFunc; |
73ee4fa7 JM |
419 | // In-order script execution tricks |
420 | if (Util.Engine.trident) { | |
421 | // For IE wait until readyState is 'loaded' before | |
422 | // appending it which will trigger execution | |
423 | // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order | |
424 | ls.push(script); | |
425 | } else { | |
426 | // For webkit and firefox set async=false and append now | |
427 | // https://developer.mozilla.org/en-US/docs/HTML/Element/script | |
428 | script.async = false; | |
429 | head.appendChild(script); | |
430 | } | |
8e2d7496 | 431 | ps.push(script); |
6f4b1e40 | 432 | } |
d21cd6c1 | 433 | }; |
6f4b1e40 | 434 | |
c77938ef | 435 | |
15046f00 | 436 | // Get DOM element position on page |
c77938ef SR |
437 | // This solution is based based on http://www.greywyvern.com/?post=331 |
438 | // Thanks to Brian Huisman AKA GreyWyvern! | |
d21cd6c1 SR |
439 | Util.getPosition = (function () { |
440 | "use strict"; | |
c77938ef | 441 | function getStyle(obj, styleProp) { |
d21cd6c1 | 442 | var y; |
c77938ef | 443 | if (obj.currentStyle) { |
d21cd6c1 | 444 | y = obj.currentStyle[styleProp]; |
c77938ef | 445 | } else if (window.getComputedStyle) |
d21cd6c1 | 446 | y = window.getComputedStyle(obj, null)[styleProp]; |
c77938ef | 447 | return y; |
d21cd6c1 | 448 | } |
c77938ef SR |
449 | |
450 | function scrollDist() { | |
451 | var myScrollTop = 0, myScrollLeft = 0; | |
452 | var html = document.getElementsByTagName('html')[0]; | |
453 | ||
454 | // get the scrollTop part | |
455 | if (html.scrollTop && document.documentElement.scrollTop) { | |
456 | myScrollTop = html.scrollTop; | |
457 | } else if (html.scrollTop || document.documentElement.scrollTop) { | |
458 | myScrollTop = html.scrollTop + document.documentElement.scrollTop; | |
459 | } else if (document.body.scrollTop) { | |
460 | myScrollTop = document.body.scrollTop; | |
461 | } else { | |
462 | myScrollTop = 0; | |
463 | } | |
464 | ||
465 | // get the scrollLeft part | |
466 | if (html.scrollLeft && document.documentElement.scrollLeft) { | |
467 | myScrollLeft = html.scrollLeft; | |
468 | } else if (html.scrollLeft || document.documentElement.scrollLeft) { | |
469 | myScrollLeft = html.scrollLeft + document.documentElement.scrollLeft; | |
470 | } else if (document.body.scrollLeft) { | |
471 | myScrollLeft = document.body.scrollLeft; | |
472 | } else { | |
473 | myScrollLeft = 0; | |
474 | } | |
475 | ||
476 | return [myScrollLeft, myScrollTop]; | |
d21cd6c1 | 477 | } |
c77938ef SR |
478 | |
479 | return function (obj) { | |
480 | var curleft = 0, curtop = 0, scr = obj, fixed = false; | |
481 | while ((scr = scr.parentNode) && scr != document.body) { | |
482 | curleft -= scr.scrollLeft || 0; | |
483 | curtop -= scr.scrollTop || 0; | |
484 | if (getStyle(scr, "position") == "fixed") { | |
485 | fixed = true; | |
486 | } | |
487 | } | |
488 | if (fixed && !window.opera) { | |
489 | var scrDist = scrollDist(); | |
490 | curleft += scrDist[0]; | |
491 | curtop += scrDist[1]; | |
492 | } | |
493 | ||
15046f00 | 494 | do { |
c77938ef SR |
495 | curleft += obj.offsetLeft; |
496 | curtop += obj.offsetTop; | |
d21cd6c1 | 497 | } while ((obj = obj.offsetParent)); |
c77938ef SR |
498 | |
499 | return {'x': curleft, 'y': curtop}; | |
500 | }; | |
501 | })(); | |
502 | ||
15046f00 JM |
503 | |
504 | // Get mouse event position in DOM element | |
125d8bbb | 505 | Util.getEventPosition = function (e, obj, scale) { |
d21cd6c1 | 506 | "use strict"; |
15046f00 JM |
507 | var evt, docX, docY, pos; |
508 | //if (!e) evt = window.event; | |
509 | evt = (e ? e : window.event); | |
ad3f7624 | 510 | evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt); |
15046f00 JM |
511 | if (evt.pageX || evt.pageY) { |
512 | docX = evt.pageX; | |
513 | docY = evt.pageY; | |
514 | } else if (evt.clientX || evt.clientY) { | |
515 | docX = evt.clientX + document.body.scrollLeft + | |
516 | document.documentElement.scrollLeft; | |
517 | docY = evt.clientY + document.body.scrollTop + | |
518 | document.documentElement.scrollTop; | |
519 | } | |
520 | pos = Util.getPosition(obj); | |
125d8bbb JM |
521 | if (typeof scale === "undefined") { |
522 | scale = 1; | |
523 | } | |
ca9a9964 PD |
524 | var realx = docX - pos.x; |
525 | var realy = docY - pos.y; | |
d21cd6c1 SR |
526 | var x = Math.max(Math.min(realx, obj.width - 1), 0); |
527 | var y = Math.max(Math.min(realy, obj.height - 1), 0); | |
ca9a9964 | 528 | return {'x': x / scale, 'y': y / scale, 'realx': realx / scale, 'realy': realy / scale}; |
15046f00 JM |
529 | }; |
530 | ||
531 | ||
532 | // Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events | |
d21cd6c1 SR |
533 | Util.addEvent = function (obj, evType, fn) { |
534 | "use strict"; | |
535 | if (obj.attachEvent) { | |
536 | var r = obj.attachEvent("on" + evType, fn); | |
15046f00 | 537 | return r; |
d21cd6c1 SR |
538 | } else if (obj.addEventListener) { |
539 | obj.addEventListener(evType, fn, false); | |
d93d3e09 | 540 | return true; |
15046f00 | 541 | } else { |
d21cd6c1 | 542 | throw new Error("Handler could not be attached"); |
15046f00 JM |
543 | } |
544 | }; | |
545 | ||
d21cd6c1 SR |
546 | Util.removeEvent = function (obj, evType, fn) { |
547 | "use strict"; | |
548 | if (obj.detachEvent) { | |
549 | var r = obj.detachEvent("on" + evType, fn); | |
15046f00 | 550 | return r; |
d21cd6c1 | 551 | } else if (obj.removeEventListener) { |
d93d3e09 JM |
552 | obj.removeEventListener(evType, fn, false); |
553 | return true; | |
15046f00 | 554 | } else { |
d21cd6c1 | 555 | throw new Error("Handler could not be removed"); |
15046f00 JM |
556 | } |
557 | }; | |
558 | ||
d21cd6c1 SR |
559 | Util.stopEvent = function (e) { |
560 | "use strict"; | |
15046f00 JM |
561 | if (e.stopPropagation) { e.stopPropagation(); } |
562 | else { e.cancelBubble = true; } | |
563 | ||
564 | if (e.preventDefault) { e.preventDefault(); } | |
565 | else { e.returnValue = false; } | |
566 | }; | |
567 | ||
568 | ||
569 | // Set browser engine versions. Based on mootools. | |
570 | Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}; | |
571 | ||
d21cd6c1 SR |
572 | (function () { |
573 | "use strict"; | |
574 | // 'presto': (function () { return (!window.opera) ? false : true; }()), | |
575 | var detectPresto = function () { | |
576 | return !!window.opera; | |
577 | }; | |
578 | ||
579 | // 'trident': (function () { return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); | |
580 | var detectTrident = function () { | |
581 | if (!window.ActiveXObject) { | |
582 | return false; | |
583 | } else { | |
584 | if (window.XMLHttpRequest) { | |
585 | return (document.querySelectorAll) ? 6 : 5; | |
586 | } else { | |
587 | return 4; | |
588 | } | |
589 | } | |
590 | }; | |
591 | ||
592 | // 'webkit': (function () { try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()), | |
593 | var detectInitialWebkit = function () { | |
594 | try { | |
595 | if (navigator.taintEnabled) { | |
596 | return false; | |
597 | } else { | |
598 | if (Util.Features.xpath) { | |
599 | return (Util.Features.query) ? 525 : 420; | |
600 | } else { | |
601 | return 419; | |
602 | } | |
603 | } | |
604 | } catch (e) { | |
605 | return false; | |
606 | } | |
607 | }; | |
608 | ||
609 | var detectActualWebkit = function (initial_ver) { | |
610 | var re = /WebKit\/([0-9\.]*) /; | |
611 | var str_ver = (navigator.userAgent.match(re) || ['', initial_ver])[1]; | |
612 | return parseFloat(str_ver, 10); | |
613 | }; | |
614 | ||
615 | // 'gecko': (function () { return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19ssName) ? 19 : 18 : 18); }()) | |
616 | var detectGecko = function () { | |
617 | /* jshint -W041 */ | |
618 | if (!document.getBoxObjectFor && window.mozInnerScreenX == null) { | |
619 | return false; | |
620 | } else { | |
621 | return (document.getElementsByClassName) ? 19 : 18; | |
622 | } | |
623 | /* jshint +W041 */ | |
624 | }; | |
625 | ||
626 | Util.Engine = { | |
627 | // Version detection break in Opera 11.60 (errors on arguments.callee.caller reference) | |
628 | //'presto': (function() { | |
629 | // return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()), | |
630 | 'presto': detectPresto(), | |
631 | 'trident': detectTrident(), | |
632 | 'webkit': detectInitialWebkit(), | |
633 | 'gecko': detectGecko(), | |
634 | }; | |
635 | ||
636 | if (Util.Engine.webkit) { | |
637 | // Extract actual webkit version if available | |
638 | Util.Engine.webkit = detectActualWebkit(Util.Engine.webkit); | |
639 | } | |
640 | })(); | |
15046f00 | 641 | |
d21cd6c1 SR |
642 | Util.Flash = (function () { |
643 | "use strict"; | |
15046f00 JM |
644 | var v, version; |
645 | try { | |
646 | v = navigator.plugins['Shockwave Flash'].description; | |
d21cd6c1 | 647 | } catch (err1) { |
15046f00 JM |
648 | try { |
649 | v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); | |
d21cd6c1 | 650 | } catch (err2) { |
15046f00 JM |
651 | v = '0 r0'; |
652 | } | |
653 | } | |
654 | version = v.match(/\d+/g); | |
655 | return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; | |
d21cd6c1 | 656 | }()); |