2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2012 Joel Martin
4 * Licensed under MPL 2.0 (see LICENSE.txt)
6 * See README.md for usage and integration instructions.
10 /*jslint bitwise: false, white: false */
11 /*global window, console, document, navigator, ActiveXObject */
13 // Globals defined here
21 Array
.prototype.push8 = function (num
) {
22 this.push(num
& 0xFF);
25 Array
.prototype.push16 = function (num
) {
26 this.push((num
>> 8) & 0xFF,
29 Array
.prototype.push32 = function (num
) {
30 this.push((num
>> 24) & 0xFF,
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
)
42 Array
.prototype.map = function(fun
/*, thisp*/)
44 var len
= this.length
;
45 if (typeof fun
!= "function")
46 throw new TypeError();
48 var res
= new Array(len
);
49 var thisp
= arguments
[1];
50 for (var i
= 0; i
< len
; i
++)
53 res
[i
] = fun
.call(thisp
, this[i
], i
, this);
61 // requestAnimationFrame shim with setTimeout fallback
64 window
.requestAnimFrame
= (function(){
65 return window
.requestAnimationFrame
||
66 window
.webkitRequestAnimationFrame
||
67 window
.mozRequestAnimationFrame
||
68 window
.oRequestAnimationFrame
||
69 window
.msRequestAnimationFrame
||
71 window
.setTimeout(callback
, 1000 / 60);
76 * ------------------------------------------------------
78 * ------------------------------------------------------
82 * Logging/debug routines
85 Util
._log_level
= 'warn';
86 Util
.init_logging = function (level
) {
87 if (typeof level
=== 'undefined') {
88 level
= Util
._log_level
;
90 Util
._log_level
= level
;
92 if (typeof window
.console
=== "undefined") {
93 if (typeof window
.opera
!== "undefined") {
95 'log' : window
.opera
.postError
,
96 'warn' : window
.opera
.postError
,
97 'error': window
.opera
.postError
};
100 'log' : function(m
) {},
101 'warn' : function(m
) {},
102 'error': function(m
) {}};
106 Util
.Debug
= Util
.Info
= Util
.Warn
= Util
.Error = function (msg
) {};
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
); };
115 throw("invalid logging type '" + level
+ "'");
118 Util
.get_logging = function () {
119 return Util
._log_level
;
121 // Initialize logging level
125 // Set configuration default for Crockford style function namespaces
126 Util
.conf_default = function(cfg
, api
, defaults
, v
, mode
, type
, defval
, desc
) {
129 // Default getter function
130 getter = function (idx
) {
131 if ((type
in {'arr':1, 'array':1}) &&
132 (typeof idx
!== 'undefined')) {
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})) {
147 } else if (type
in {'integer':1, 'int':1}) {
148 val
= parseInt(val
, 10);
149 } else if (type
=== 'str') {
151 } else if (type
=== 'func') {
153 val = function () {};
156 if (typeof idx
!== 'undefined') {
163 // Set the description
164 api
[v
+ '_description'] = desc
;
166 // Set the getter function
167 if (typeof api
['get_' + v
] === 'undefined') {
168 api
['get_' + v
] = getter
;
171 // Set the setter function with extra sanity checks
172 if (typeof api
['set_' + v
] === 'undefined') {
173 api
['set_' + v
] = function (val
, idx
) {
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");
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
))) {
191 // Coerce existing setting to the right type
192 //Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]);
196 // Set group of configuration defaults
197 Util
.conf_defaults = function(cfg
, api
, defaults
, arr
) {
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]);
207 * Cross-browser routines
211 // Dynamically load scripts without using document.write()
212 // Reference: http://unixpapa.com/js/dyna.html
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/";
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
);
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);
240 // Call window.onscriptsload after last script loads
241 if (ps
.length
=== 0 && window
.onscriptsload
) {
242 window
.onscriptsload();
249 // Get DOM element position on page
250 Util
.getPosition = function (obj
) {
252 if (obj
.offsetParent
) {
256 obj
= obj
.offsetParent
;
259 return {'x': x
, 'y': y
};
262 // Get mouse event position in DOM element
263 Util
.getEventPosition = function (e
, obj
, scale
) {
264 var evt
, docX
, docY
, pos
;
265 //if (!e) evt = window.event;
266 evt
= (e
? e
: window
.event
);
267 evt
= (evt
.changedTouches
? evt
.changedTouches
[0] : evt
.touches
? evt
.touches
[0] : evt
);
268 if (evt
.pageX
|| 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
;
277 pos
= Util
.getPosition(obj
);
278 if (typeof scale
=== "undefined") {
281 return {'x': (docX
- pos
.x
) / scale
, 'y': (docY
- pos
.y
) / scale
};
285 // Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
286 Util
.addEvent = function (obj
, evType
, fn
){
287 if (obj
.attachEvent
){
288 var r
= obj
.attachEvent("on"+evType
, fn
);
290 } else if (obj
.addEventListener
){
291 obj
.addEventListener(evType
, fn
, false);
294 throw("Handler could not be attached");
298 Util
.removeEvent = function(obj
, evType
, fn
){
299 if (obj
.detachEvent
){
300 var r
= obj
.detachEvent("on"+evType
, fn
);
302 } else if (obj
.removeEventListener
){
303 obj
.removeEventListener(evType
, fn
, false);
306 throw("Handler could not be removed");
310 Util
.stopEvent = function(e
) {
311 if (e
.stopPropagation
) { e
.stopPropagation(); }
312 else { e
.cancelBubble
= true; }
314 if (e
.preventDefault
) { e
.preventDefault(); }
315 else { e
.returnValue
= false; }
319 // Set browser engine versions. Based on mootools.
320 Util
.Features
= {xpath
: !!(document
.evaluate
), air
: !!(window
.runtime
), query
: !!(document
.querySelector
)};
323 // Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
324 //'presto': (function() {
325 // return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
326 'presto': (function() { return (!window
.opera
) ? false : true; }()),
328 'trident': (function() {
329 return (!window
.ActiveXObject
) ? false : ((window
.XMLHttpRequest
) ? ((document
.querySelectorAll
) ? 6 : 5) : 4); }()),
330 'webkit': (function() {
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); }()),
334 'gecko': (function() {
335 return (!document
.getBoxObjectFor
&& window
.mozInnerScreenX
== null) ? false : ((document
.getElementsByClassName
) ? 19 : 18); }())
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
);
346 Util
.Flash
= (function(){
349 v
= navigator
.plugins
['Shockwave Flash'].description
;
352 v
= new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
357 version
= v
.match(/\d+/g);
358 return {version
: parseInt(version
[0] || 0 + '.' + version
[1], 10) || 0, build
: parseInt(version
[2], 10) || 0};