util.js 10 KB
Newer Older
1
/*
2 3
 * noVNC: HTML5 VNC client
 * Copyright (C) 2010 Joel Martin
Joel Martin's avatar
Joel Martin committed
4
 * Licensed under LGPL-3 (see LICENSE.txt)
5 6
 *
 * See README.md for usage and integration instructions.
7 8
 */

9 10
"use strict";
/*jslint bitwise: false, white: false */
11
/*global window, console, document, navigator, ActiveXObject*/
12

13 14 15
// Globals defined here
var Util = {}, $;

16

17 18 19
/*
 * Simple DOM selector by ID
 */
20 21 22 23 24 25 26 27 28 29 30 31
if (!window.$) {
    $ = function (id) {
        if (document.getElementById) {
            return document.getElementById(id);
        } else if (document.all) {
            return document.all[id];
        } else if (document.layers) {
            return document.layers[id];
        }
        return undefined;
    };
}
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

/*
 * Make arrays quack
 */

Array.prototype.push8 = function (num) {
    this.push(num & 0xFF);
};

Array.prototype.push16 = function (num) {
    this.push((num >> 8) & 0xFF,
              (num     ) & 0xFF  );
};
Array.prototype.push32 = function (num) {
    this.push((num >> 24) & 0xFF,
              (num >> 16) & 0xFF,
              (num >>  8) & 0xFF,
              (num      ) & 0xFF  );
};

52 53 54 55 56 57
/* 
 * ------------------------------------------------------
 * Namespaced in Util
 * ------------------------------------------------------
 */

58 59 60 61
/*
 * Logging/debug routines
 */

62
Util._log_level = 'warn';
63
Util.init_logging = function (level) {
64 65 66 67 68 69 70 71
    if (typeof level === 'undefined') {
        Util._log_level = (document.location.href.match(
                /logging=([A-Za-z0-9\._\-]*)/) ||
                ['', Util._log_level])[1];
        level = Util._log_level;
    } else {
        Util._log_level = level;
    }
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    if (typeof window.console === "undefined") {
        if (typeof window.opera !== "undefined") {
            window.console = {
                'log'  : window.opera.postError,
                'warn' : window.opera.postError,
                'error': window.opera.postError };
        } else {
            window.console = {
                'log'  : function(m) {},
                'warn' : function(m) {},
                'error': function(m) {}};
        }
    }

    Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
    switch (level) {
        case 'debug': Util.Debug = function (msg) { console.log(msg); };
        case 'info':  Util.Info  = function (msg) { console.log(msg); };
        case 'warn':  Util.Warn  = function (msg) { console.warn(msg); };
        case 'error': Util.Error = function (msg) { console.error(msg); };
        case 'none':
            break;
        default:
            throw("invalid logging type '" + level + "'");
    }
};
98 99 100
Util.get_logging = function () {
        return Util._log_level;
    }
101
// Initialize logging level
102
Util.init_logging();
103

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
Util.dirObj = function (obj, depth, parent) {
    var i, msg = "", val = "";
    if (! depth) { depth=2; }
    if (! parent) { parent= ""; }

    // Print the properties of the passed-in object 
    for (i in obj) {
        if ((depth > 1) && (typeof obj[i] === "object")) { 
            // Recurse attributes that are objects
            msg += Util.dirObj(obj[i], depth-1, parent + "." + i);
        } else {
            //val = new String(obj[i]).replace("\n", " ");
            val = obj[i].toString().replace("\n", " ");
            if (val.length > 30) {
                val = val.substr(0,30) + "...";
            } 
            msg += parent + "." + i + ": " + val + "\n";
        }
    }
    return msg;
};

126 127 128 129 130 131 132
// Read a query string variable
Util.getQueryVar = function(name, defVal) {
    var re = new RegExp('[?][^#]*' + name + '=([^&#]*)');
    if (typeof defVal === 'undefined') { defVal = null; }
    return (document.location.href.match(re) || ['',defVal])[1];
};

133
// Set defaults for Crockford style function namespaces
134 135 136
Util.conf_default = function(cfg, api, v, type, defval, desc) {
    // Description
    api['get_' + v + '_desc'] = desc;
137 138 139 140 141 142 143 144 145
    // Default getter
    if (typeof api['get_' + v] === 'undefined') {
        api['get_' + v] = function () {
                return cfg[v];
            };
    }
    // Default setter
    if (typeof api['set_' + v] === 'undefined') {
        api['set_' + v] = function (val) {
146
                if (type in {'boolean':1, 'bool':1}) {
147 148 149 150 151
                    if ((!val) || (val in {'0':1, 'no':1, 'false':1})) {
                        val = false;
                    } else {
                        val = true;
                    }
152 153
                } else if (type in {'integer':1, 'int':1}) {
                    val = parseInt(val, 10);
154
                }
155 156 157
                cfg[v] = val;
            };
    }
158 159 160 161 162 163 164 165

    if (typeof cfg[v] === 'undefined') {
        // Set to default
        api['set_' + v](defval);
    } else {
        // Coerce existing setting to the right type
        api['set_' + v](cfg[v]);
    }
166 167 168
};


169

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
/*
 * Cross-browser routines
 */

// Get DOM element position on page
Util.getPosition = function (obj) {
    var x = 0, y = 0;
    if (obj.offsetParent) {
        do {
            x += obj.offsetLeft;
            y += obj.offsetTop;
            obj = obj.offsetParent;
        } while (obj);
    }
    return {'x': x, 'y': y};
};

// Get mouse event position in DOM element
188
Util.getEventPosition = function (e, obj, scale) {
189 190 191 192 193 194 195 196 197 198 199 200 201
    var evt, docX, docY, pos;
    //if (!e) evt = window.event;
    evt = (e ? e : window.event);
    if (evt.pageX || evt.pageY) {
        docX = evt.pageX;
        docY = evt.pageY;
    } else if (evt.clientX || evt.clientY) {
        docX = evt.clientX + document.body.scrollLeft +
            document.documentElement.scrollLeft;
        docY = evt.clientY + document.body.scrollTop +
            document.documentElement.scrollTop;
    }
    pos = Util.getPosition(obj);
202 203 204 205
    if (typeof scale === "undefined") {
        scale = 1;
    }
    return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale};
206 207 208 209 210
};


// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
Util.addEvent = function (obj, evType, fn){
211
    if (obj.attachEvent){
212 213
        var r = obj.attachEvent("on"+evType, fn);
        return r;
214 215 216
    } else if (obj.addEventListener){
        obj.addEventListener(evType, fn, false); 
        return true;
217 218 219 220 221 222
    } else {
        throw("Handler could not be attached");
    }
};

Util.removeEvent = function(obj, evType, fn){
223
    if (obj.detachEvent){
224 225
        var r = obj.detachEvent("on"+evType, fn);
        return r;
226 227 228
    } else if (obj.removeEventListener){
        obj.removeEventListener(evType, fn, false);
        return true;
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
    } else {
        throw("Handler could not be removed");
    }
};

Util.stopEvent = function(e) {
    if (e.stopPropagation) { e.stopPropagation(); }
    else                   { e.cancelBubble = true; }

    if (e.preventDefault)  { e.preventDefault(); }
    else                   { e.returnValue = false; }
};


// Set browser engine versions. Based on mootools.
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};

Util.Engine = {
    'presto': (function() {
            return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
    'trident': (function() {
            return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
    'webkit': (function() {
252 253 254
            try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
    //'webkit': (function() {
    //        return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()),
255
    'gecko': (function() {
256
            return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }())
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
};

Util.Flash = (function(){
    var v, version;
    try {
        v = navigator.plugins['Shockwave Flash'].description;
    } catch(err1) {
        try {
            v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
        } catch(err2) {
            v = '0 r0';
        }
    }
    version = v.match(/\d+/g);
    return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
}()); 

274 275 276
/*
 * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
 */
277

278 279
// No days means only for this browser session
Util.createCookie = function(name,value,days) {
280
    var date, expires;
281
    if (days) {
282
        date = new Date();
283
        date.setTime(date.getTime()+(days*24*60*60*1000));
284 285 286 287
        expires = "; expires="+date.toGMTString();
    }
    else {
        expires = "";
288 289 290 291 292
    }
    document.cookie = name+"="+value+expires+"; path=/";
};

Util.readCookie = function(name, defaultValue) {
293 294 295
    var i, c, nameEQ = name + "=", ca = document.cookie.split(';');
    for(i=0; i < ca.length; i += 1) {
        c = ca[i];
296 297
        while (c.charAt(0) === ' ') { c = c.substring(1,c.length); }
        if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); }
298 299 300 301 302
    }
    return (typeof defaultValue !== 'undefined') ? defaultValue : null;
};

Util.eraseCookie = function(name) {
303
    Util.createCookie(name,"",-1);
304 305 306 307 308 309
};

/*
 * Alternate stylesheet selection
 */
Util.getStylesheets = function() { var i, links, sheets = [];
310
    links = document.getElementsByTagName("link");
311
    for (i = 0; i < links.length; i += 1) {
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
        if (links[i].title &&
            links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) {
            sheets.push(links[i]);
        }
    }
    return sheets;
};

// No sheet means try and use value from cookie, null sheet used to
// clear all alternates.
Util.selectStylesheet = function(sheet) {
    var i, link, sheets = Util.getStylesheets();
    if (typeof sheet === 'undefined') {
        sheet = 'default';
    }
327
    for (i=0; i < sheets.length; i += 1) {
328 329 330 331 332
        link = sheets[i];
        if (link.title === sheet) {    
            Util.Debug("Using stylesheet " + sheet);
            link.disabled = false;
        } else {
333
            //Util.Debug("Skipping stylesheet " + link.title);
334 335 336 337 338
            link.disabled = true;
        }
    }
    return sheet;
};