1/* 2 * noVNC: HTML5 VNC client 3 * Copyright (C) 2011 Joel Martin 4 * Licensed under LGPL-3 (see LICENSE.txt) 5 * 6 * See README.md for usage and integration instructions. 7 */ 8 9"use strict"; 10/*jslint bitwise: false, white: false */ 11/*global window, console, document, navigator, ActiveXObject */ 12 13// Globals defined here 14var Util = {}; 15 16 17/* 18 * Make arrays quack 19 */ 20 21Array.prototype.push8 = function (num) { 22 this.push(num & 0xFF); 23}; 24 25Array.prototype.push16 = function (num) { 26 this.push((num >> 8) & 0xFF, 27 (num ) & 0xFF ); 28}; 29Array.prototype.push32 = function (num) { 30 this.push((num >> 24) & 0xFF, 31 (num >> 16) & 0xFF, 32 (num >> 8) & 0xFF, 33 (num ) & 0xFF ); 34}; 35 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 40if (!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 60/* 61 * ------------------------------------------------------ 62 * Namespaced in Util 63 * ------------------------------------------------------ 64 */ 65 66/* 67 * Logging/debug routines 68 */ 69 70Util._log_level = 'warn'; 71Util.init_logging = function (level) { 72 if (typeof level === 'undefined') { 73 level = Util._log_level; 74 } else { 75 Util._log_level = level; 76 } 77 if (typeof window.console === "undefined") { 78 if (typeof window.opera !== "undefined") { 79 window.console = { 80 'log' : window.opera.postError, 81 'warn' : window.opera.postError, 82 'error': window.opera.postError }; 83 } else { 84 window.console = { 85 'log' : function(m) {}, 86 'warn' : function(m) {}, 87 'error': function(m) {}}; 88 } 89 } 90 91 Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {}; 92 switch (level) { 93 case 'debug': Util.Debug = function (msg) { console.log(msg); }; 94 case 'info': Util.Info = function (msg) { console.log(msg); }; 95 case 'warn': Util.Warn = function (msg) { console.warn(msg); }; 96 case 'error': Util.Error = function (msg) { console.error(msg); }; 97 case 'none': 98 break; 99 default: 100 throw("invalid logging type '" + level + "'"); 101 } 102}; 103Util.get_logging = function () { 104 return Util._log_level; 105}; 106// Initialize logging level 107Util.init_logging(); 108 109 110// Set configuration default for Crockford style function namespaces 111Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) { 112 var getter, setter; 113 114 // Default getter function 115 getter = function (idx) { 116 if ((type in {'arr':1, 'array':1}) && 117 (typeof idx !== 'undefined')) { 118 return cfg[v][idx]; 119 } else { 120 return cfg[v]; 121 } 122 }; 123 124 // Default setter function 125 setter = function (val, idx) { 126 if (type in {'boolean':1, 'bool':1}) { 127 if ((!val) || (val in {'0':1, 'no':1, 'false':1})) { 128 val = false; 129 } else { 130 val = true; 131 } 132 } else if (type in {'integer':1, 'int':1}) { 133 val = parseInt(val, 10); 134 } else if (type === 'func') { 135 if (!val) { 136 val = function () {}; 137 } 138 } 139 if (typeof idx !== 'undefined') { 140 cfg[v][idx] = val; 141 } else { 142 cfg[v] = val; 143 } 144 }; 145 146 // Set the description 147 api[v + '_description'] = desc; 148 149 // Set the getter function 150 if (typeof api['get_' + v] === 'undefined') { 151 api['get_' + v] = getter; 152 } 153 154 // Set the setter function with extra sanity checks 155 if (typeof api['set_' + v] === 'undefined') { 156 api['set_' + v] = function (val, idx) { 157 if (mode in {'RO':1, 'ro':1}) { 158 throw(v + " is read-only"); 159 } else if ((mode in {'WO':1, 'wo':1}) && 160 (typeof cfg[v] !== 'undefined')) { 161 throw(v + " can only be set once"); 162 } 163 setter(val, idx); 164 }; 165 } 166 167 // Set the default value 168 if (typeof defaults[v] !== 'undefined') { 169 defval = defaults[v]; 170 } else if ((type in {'arr':1, 'array':1}) && 171 (! (defval instanceof Array))) { 172 defval = []; 173 } 174 // Coerce existing setting to the right type 175 //Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]); 176 setter(defval); 177}; 178 179// Set group of configuration defaults 180Util.conf_defaults = function(cfg, api, defaults, arr) { 181 var i; 182 for (i = 0; i < arr.length; i++) { 183 Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1], 184 arr[i][2], arr[i][3], arr[i][4]); 185 } 186}; 187 188 189/* 190 * Cross-browser routines 191 */ 192 193// Get DOM element position on page 194Util.getPosition = function (obj) { 195 var x = 0, y = 0; 196 if (obj.offsetParent) { 197 do { 198 x += obj.offsetLeft; 199 y += obj.offsetTop; 200 obj = obj.offsetParent; 201 } while (obj); 202 } 203 return {'x': x, 'y': y}; 204}; 205 206// Get mouse event position in DOM element 207Util.getEventPosition = function (e, obj, scale) { 208 var evt, docX, docY, pos; 209 //if (!e) evt = window.event; 210 evt = (e ? e : window.event); 211 evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt); 212 if (evt.pageX || evt.pageY) { 213 docX = evt.pageX; 214 docY = evt.pageY; 215 } else if (evt.clientX || evt.clientY) { 216 docX = evt.clientX + document.body.scrollLeft + 217 document.documentElement.scrollLeft; 218 docY = evt.clientY + document.body.scrollTop + 219 document.documentElement.scrollTop; 220 } 221 pos = Util.getPosition(obj); 222 if (typeof scale === "undefined") { 223 scale = 1; 224 } 225 return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale}; 226}; 227 228 229// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events 230Util.addEvent = function (obj, evType, fn){ 231 if (obj.attachEvent){ 232 var r = obj.attachEvent("on"+evType, fn); 233 return r; 234 } else if (obj.addEventListener){ 235 obj.addEventListener(evType, fn, false); 236 return true; 237 } else { 238 throw("Handler could not be attached"); 239 } 240}; 241 242Util.removeEvent = function(obj, evType, fn){ 243 if (obj.detachEvent){ 244 var r = obj.detachEvent("on"+evType, fn); 245 return r; 246 } else if (obj.removeEventListener){ 247 obj.removeEventListener(evType, fn, false); 248 return true; 249 } else { 250 throw("Handler could not be removed"); 251 } 252}; 253 254Util.stopEvent = function(e) { 255 if (e.stopPropagation) { e.stopPropagation(); } 256 else { e.cancelBubble = true; } 257 258 if (e.preventDefault) { e.preventDefault(); } 259 else { e.returnValue = false; } 260}; 261 262 263// Set browser engine versions. Based on mootools. 264Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}; 265 266Util.Engine = { 267 // Version detection break in Opera 11.60 (errors on arguments.callee.caller reference) 268 //'presto': (function() { 269 // return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()), 270 'presto': (function() { return (!window.opera) ? false : true; }()), 271 272 'trident': (function() { 273 return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()), 274 'webkit': (function() { 275 try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()), 276 //'webkit': (function() { 277 // return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()), 278 'gecko': (function() { 279 return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }()) 280}; 281if (Util.Engine.webkit) { 282 // Extract actual webkit version if available 283 Util.Engine.webkit = (function(v) { 284 var re = new RegExp('WebKit/([0-9\.]*) '); 285 v = (navigator.userAgent.match(re) || ['', v])[1]; 286 return parseFloat(v, 10); 287 })(Util.Engine.webkit); 288} 289 290Util.Flash = (function(){ 291 var v, version; 292 try { 293 v = navigator.plugins['Shockwave Flash'].description; 294 } catch(err1) { 295 try { 296 v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); 297 } catch(err2) { 298 v = '0 r0'; 299 } 300 } 301 version = v.match(/\d+/g); 302 return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; 303}()); 304