/**
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2009 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Viktor Pracht <viktor.pracht@open-xchange.com>
 */

/**
 * Creates the prototype object of a subclass.
 * @param {Function} parent The constructor function of the superclass.
 * @param {Object} prototype The prototype object of the subclass, containing
 * only new and overridden members.
 * @type Object
 * @return The prototype object with all inherited members added to it.
 */
function extend(parent, prototype) {
    for (var i in parent.prototype)
        if (!(i in prototype)) prototype[i] = parent.prototype[i];
    return prototype;
}

/**
 * Empty function. Does nothing.
 */
function emptyFunction() {}

/**
 * Identity function. Returns its first parameter.
 */
function identity(x) { return x; }

/**
 * Constant function.
 * Returns a new function which returns the first parameter of this function.
 * @param x
 * @type Function
 * @return A function which returns x.
 */
function constant(x) { return function() { return x; }; }

/**
 * Returns whether an object is empty.
 * @param {Object} x The tested object
 * @type Boolean
 * @return True if the object does not have enumerable properties,
 * false otherwise.
 */
function isEmpty(x) {
    for (var i in x) return false;
    return true;
}

/** @ignore */
function assert(value) { if (!value) alert("Assertion failed"); }

/**
 * Internet Explorer version number, or undefined if using a real browser.
 * Since comparisons with undefined always return false, checks involving this
 * variable should use the "<" or "<=" operators, e. g. "if (IE < 8)".
 */
var IE = (navigator.appName != "Microsoft Internet Explorer") ? undefined
    : Number(navigator.appVersion.match(/MSIE (\d+\.\d+)/)[1]);

/**
 * Boolean indicating the MacOS platform. If true, selections use the Meta key
 * instead of the Ctrl key.
 */
var Mac = navigator.platform.substring(0, 3) == "Mac";

/**
 * Creates a function which can add or remove a CSS class to a DOM node.
 * When adding, a class name is not duplicated if already present.
 * When removing, all instances of a class name are removed if present multiple
 * times.
 * @param {String} name The class name to add or remove. It must not contain any
 * characters which have special meaning in regular expressions.
 * @type Function
 * @return A function which accepts two parameters:<ul> 
 * <li>{DOM node} node The DOM node to which the class name is applied.</li>
 * <li>{Boolean} set Whether the class name should be added (true) or
 * removed (false).</li>
 * @ignore
 */
function classNameSetter(name) {
    var regex = new RegExp("(\\s)\\s*" + name + "\\s+|\\s*" + name + "\\s*",
                           "g");
    return function (node, set) {
        var c = String(node.className);
        regex.lastIndex = 0;
        if (set) {
            if (!regex.test(c)) c = c ? c + " " + name : name;
        } else {
            c = c.replace(regex, "$1");
        }
        node.className = c;
        return c;
    };
}

/**
 * Escapes a string to be included in an HTML file.
 * The characters '<', '>', '&', '"' and "'" are replaced by their HTML entity
 * references.
 * @param {String} s The string to escape.
 * @type String
 * @return The escaped string.
 */
function escapeHTML(s) {
    return s.replace(/[<>&"]/g, function(c) { return htmlEscapes[c]; });
}
var htmlEscapes = { "<": "&lt;", ">": "&gt;", "&": "&amp;", "\"": "&quot;" };

/**
 * Escapes a string to be included in a regular expression.
 * @param {String} s The string to escape.
 * @type String
 * @return The escaped string.
 */
function escapeRegExp(s) { return s.replace(/([|^$\\.*+?()[\]{}])/g, '\\$1'); }