Home | History | Annotate | Download | only in include
      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
     14 var Util = {};
     15 
     16 
     17 /*
     18  * Make arrays quack
     19  */
     20 
     21 Array.prototype.push8 = function (num) {
     22     this.push(num & 0xFF);
     23 };
     24 
     25 Array.prototype.push16 = function (num) {
     26     this.push((num >> 8) & 0xFF,
     27               (num     ) & 0xFF  );
     28 };
     29 Array.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
     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 
     60 /*
     61  * ------------------------------------------------------
     62  * Namespaced in Util
     63  * ------------------------------------------------------
     64  */
     65 
     66 /*
     67  * Logging/debug routines
     68  */
     69 
     70 Util._log_level = 'warn';
     71 Util.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 };
    103 Util.get_logging = function () {
    104     return Util._log_level;
    105 };
    106 // Initialize logging level
    107 Util.init_logging();
    108 
    109 
    110 // Set configuration default for Crockford style function namespaces
    111 Util.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
    180 Util.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
    194 Util.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
    207 Util.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
    230 Util.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 
    242 Util.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 
    254 Util.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.
    264 Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
    265 
    266 Util.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 };
    281 if (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 
    290 Util.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