var isFirefox = navigator.userAgent.indexOf("Firefox") != -1;
var isIE = navigator.userAgent.indexOf("MSIE") != -1;
var isOpera = navigator.userAgent.indexOf("Opera") != -1;
var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1;

/**
 * Alias for 'document.getElementById'
 *
 * @param	{String} id					ID of the element to get
 * @return	{Node}						The desired element
 */
function $(id)
{
	return document.getElementById(id);
}

/**
 * Return a random integer between "min" and "max"
 *
 * @param   {Number} min                The lowest possible value (<b>optional</b>: <i>0</i>)
 * @param   {Number} max                The highest possible value
 * @return  {Number}                    The generated random number
 */
Math.rand = function (min, max)
{
    if (max == null) {
        max = min;
        min = 0;
    }
    return Math.floor(min + Math.random() * (max - min + 1));
};

/**
 * Extend the given class
 *
 * @param   {Function} parent           The class to extend
 */
Function.prototype.extend = function (parent)
{
	this.prototype = new parent();
	this.prototype.constructor = this;
};

/**
 * Determine whether an array contains the given value
 *
 * @param   {mixed} value               The value to find
 * @param   {Array} array               The array to search in
 * @return  {Boolean}                   Does the array contain the value?
 */
function inArray(value, array)
{
    for (var i = 0; i < array.length; i++) {
        if (array[i] === value) {
            return true;
        }
    }
    return false;
}

/**
 * Remove the given value from the array
 *
 * @param	{mixed} value				The value to remove
 * @param	{Array}	value				The array to remove the value from
 * @param	{Boolean} [remove_all=true] Remove all matching elements?
 */
function removeFromArray(value, array, remove_all)
{
	remove_all = remove_all == null ? true : remove_all;

	var i = 0;
	while (i < array.length) {
		if (array[i] === value) {
			array.splice(i, 1);
			if (!remove_all) {
				break;
			}
		} else {
			i++;
		}
	}
}

/**
 * @class       Exception to throw when invalid arguments are passed to a function
 *
 * @extends     Error
 * @constructor
 * @param       {String} message        The error message
 */
var InvalidArgumentError = function (message)
{
    /**
     * The error message
     * @type    String
     */
    this.message = message;
    /**
     * The name of the exception
     * @type    String
     */
    this.name = 'InvalidArgumentError';

};
InvalidArgumentError.prototype = new Error();

/**
 * Create an XMLHttpRequest object
 *
 * @return  {XMLHttpRequest}            The XMLHttpRequest object
 */
function createXMLHttpRequest()
{
    var xmlhttp;

    if (typeof XMLHttpRequest != 'undefined') {
        xmlhttp = new XMLHttpRequest();
    }
    if (!xmlhttp) {
        try {
            xmlhttp = new ActiveXObject('Msxml2.XMLHTTP');
        } catch (e) {
            try {
                xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
            } catch (e) {
            }
        }
    }
    return xmlhttp;
}

/**
 * Send an AJAX request
 *
 * @param   {String} url                The request URL
 * @param   {Object, null} obj          The object containing the callback function (null if function is static)
 * @param   {String, null} funcname     The name of the callback function (null if no callback needed)
 * @param   {Array} [params]            Parameters to be passed to the function (<b>optional</b>)
 * @param   {String} [postdata]         Data to send by POST method (<b>optional</b>)
 * @param   {Boolean} [async=true]      Send request asynchronously (<b>optional</b>: <i>true</i>)
 */
function sendAJAXRequest(url, obj, funcname, params, postdata, async)
{
    function executeCallbackFunc(obj, funcname, params)
    {
        if (obj != null) {
            obj[funcname].apply(obj, [xmlhttp].concat(params));
        } else {
            fp = eval(funcname);   // Get function pointer for given function name
            fp.apply(null, [xmlhttp].concat(params));
        }
    }

    params = params == null ? new Array() : params;
    async  = async == null ? true : async;

    var method  = postdata ? 'POST' : 'GET';
    var xmlhttp = createXMLHttpRequest();

    xmlhttp.open(method, url, async);
    if (postdata) {
        xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    }
    if (async) {
        xmlhttp.onreadystatechange = function () {
            if (xmlhttp.readyState == 4) {
                if (xmlhttp.status == 200) {
                    // Execute the callback function
                    if (funcname != null) {
                    	executeCallbackFunc(obj, funcname, params);
                    }
                } else if (typeof console != 'undefined') {
                    console.error('Could not load URL \'' + url + '\', response code: ' + xmlhttp.status + ' ' + xmlhttp.statusText);
                }
            }
        }
    }
    xmlhttp.send(postdata);   // Send the request to the server
    if (!async) {
        if (xmlhttp.status == 200) {
            // Execute the callback function
            if (funcname != null) {
            	executeCallbackFunc(obj, funcname, params);
            }
        } else if (typeof console != 'undefined') {
            console.error('Could not load URL \'' + url + '\', response code: ' + xmlhttp.status + ' ' + xmlhttp.statusText);
        }
    }
}

/**
 * Check if an URL exists
 *
 * @param   {String} url             	The URL to check
 * @return	{Boolean}					Does the URL exist?
 */
function urlExists(url)
{
    var xmlhttp = createXMLHttpRequest();
    // Get HTTP header in synchronous mode
    xmlhttp.open('HEAD', url, false);
    xmlhttp.send(null);
    return xmlhttp.status == 200;
}

/**
 * Determine the dimensions of the given element
 *
 * @param   {mixed} element             Element or ID of the element to get the dimensions of
 * @return  {Object}                    The element's dimensions
 */
function getElementDimensions(element)
{
    if (typeof element != 'object') {
        element = document.getElementById(element);
    }

    var old_elem    = element;
    var old_display = element.style.display;
    element.style.display = '';   // Temporarily show element to determine its size

    var width  = element.offsetWidth;
    var height = element.offsetHeight;

    var left = 0;
    var top  = 0;
    if (element.offsetParent) {
        left = element.offsetLeft;
        top  = element.offsetTop;
        while (element = element.offsetParent) {
            left += element.offsetLeft;
            top  += element.offsetTop;
        }
    }

    old_elem.style.display = old_display;

    return {
        'left'   : left,
        'top'    : top,
        'width'  : width,
        'height' : height
    };
}

/**
 * Determine the document size and scrollbar positions
 *
 * @return  {Object}                    The document dimensions (height, width, offsetX, offsetY)
 */
function getDocumentDimensions()
{
    var width, height, sWidth, sHeight, offsetX, offsetY;

    if (document.compatMode && document.compatMode != 'BackCompat'
        && navigator.userAgent.search('KHTML|AppleWebKit|Safari') != -1)
    {
        width   = document.documentElement.clientWidth;
        height  = document.documentElement.clientHeight;
        offsetX = document.body.scrollLeft;
        offsetY = document.body.scrollTop;
        sWidth  = document.documentElement.scrollWidth;
        sHeight = document.documentElement.scrollHeight;
    } else if (document.compatMode && document.compatMode != 'BackCompat'
        && navigator.userAgent.indexOf('Opera') == -1)
    {
        width   = document.documentElement.clientWidth;
        height  = document.documentElement.clientHeight;
        offsetX = document.documentElement.scrollLeft;
        offsetY = document.documentElement.scrollTop;
        sWidth  = document.documentElement.scrollWidth;
        sHeight = document.documentElement.scrollHeight;
    } else {
        width   = document.body.clientWidth;
        height  = document.body.clientHeight;
        offsetX = document.body.scrollLeft;
        offsetY = document.body.scrollTop;
        sWidth  = document.body.scrollWidth;
        sHeight = document.body.scrollHeight;
    }

    return {
        'width'        : width,
        'height'       : height,
        'scrollWidth'  : sWidth,
        'scrollHeight' : sHeight,
        'offsetX'      : offsetX,
        'offsetY'      : offsetY
    };
}

/**
 * Center an element in another element
 *
 * @param   {mixed} inner               Element or ID of the element to center
 * @param   {mixed} outer               Enclosing element or its ID
 */
function centerElement(inner, outer)
{
    if (typeof inner != 'object') {
        inner = document.getElementById(inner);
    }

    var dim_inner = getElementDimensions(inner);
    var dim_outer = getElementDimensions(outer);

    var left = dim_outer.left + dim_outer.width / 2 - dim_inner.width / 2;
    var top  = dim_outer.top + dim_outer.height / 2 - dim_inner.height / 2;

    inner.style.position = 'absolute';
    inner.style.left     = left + 'px';
    inner.style.top      = top + 'px';
}

/**
 * Center an element on the screen
 *
 * @param   {mixed} element             Element or ID of the element to center
 */
function centerOnScreen(element)
{
    if (typeof element != 'object') {
        element = document.getElementById(element);
    }

    var elem_dim = getElementDimensions(element);
    var doc_dim  = getDocumentDimensions();

    var left = doc_dim.width / 2 - elem_dim.width / 2 + doc_dim.offsetX;
    var top  = doc_dim.height / 2 - elem_dim.height / 2 + doc_dim.offsetY;

    element.style.position = 'absolute';
    element.style.left     = left + 'px';
    element.style.top      = top + 'px';
}

/**
 * Execute the given function(s)
 *
 * @param	{Array} funcs				funcs[0]: The object containing the function (NULL for static function)
 * 										funcs[1]: The name of the function as string
 * 										funcs[2]: Array of parameters for the function (<b>optional</b>)
 */
function runFuncs(funcs)
{
	for (var i = 0; i < funcs.length; i++)
	{
		var obj    = funcs[i][0];
		var func   = funcs[i][1];
		var params = funcs[i][2] ? funcs[i][2] : new Array();

		if (obj != null) {
			obj[func].apply(obj, params);
		} else {
			var fp = eval(func);   // Get function pointer for given function name
			fp.apply(null, params);
		}
	}
}

/**
 * http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
 */
function getStyle(oElm, strCssRule){
	var strValue = "";
	if(document.defaultView && document.defaultView.getComputedStyle){
		strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
	}
	else if(oElm.currentStyle){
		strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
			return p1.toUpperCase();
		});
		strValue = oElm.currentStyle[strCssRule];
	}
	return strValue;
}

/**
 * Serialize the given value
 *
 * @param   {mixed} value               The value to serialize
 * @return  {String}                    The serialized value
 * @throws  {TypeError}
 */
function serialize(value)
{
    if (value === null) {
        return 'N;';
    }

    if (value instanceof Array) {
        var str = 'a:' + value.length + ':{';
        for (var i = 0; i < value.length; i++) {
            str += serialize(i) + serialize(value[i]);
        }
        return str + '}';
    }

    if (typeof value == 'number') {
        if (Math.floor(value) == value) {
            return 'i:' + value + ';';
        } else {
            return 'd:' + value + ';';
        }
    }

    if (typeof value == 'string') {
        return 's:' + value.length + ':"' + value + '";';
    }

    if (typeof value == 'boolean') {
        return 'b:' + Number(value) + ';';
    }

    if (typeof value == 'object') {
        if (value.serialize) {
            return value.serialize();
        } else {
            var len  = 0;
            var str  = '';
            var name = value.getClassName ? value.getClassName() : 'Object';
            for (var i in value) {
                if (typeof value[i] != 'function') {
                    str += serialize(i) + serialize(value[i]);
                    len++;
                }
            }
            return 'O:' + name.length + ':"' + name + '":' + len + ':{' + str + '}';
        }
    }

    throw new TypeError('Could not serialize value: ' + value);
}

/**
 * Set the opacity of the given element
 *
 * @param   {Node, String} element      Element or ID of the element to set the opacity of
 * @param   {Number} opacity            The opacity value to set (0-100)
 */
function setOpacity(element, opacity)
{
    if (typeof element != 'object') {
        element = document.getElementById(element);
    }
    if (opacity > 100) {
        opacity = 100;
    }
    if (opacity < 0) {
        opacity = 0;
    }

    element.style.filter       = 'alpha(opacity:' + opacity + ')';
    element.style.KHTMLOpacity = opacity / 100;
    element.style.MozOpacity   = opacity / 100;
    element.style.opacity      = opacity / 100;
}

/**
 * Fade an element in smoothly
 *
 * @param   {Node, String} element      Element or ID of the element to fade in
 * @param   {Number} time               The fade-in time [msec]
 * @param   {Number} [max_opacity=100]  The maximum opacity to reach (0-100) (<b>optional</b>: <i>100</i>)
 * @param   {Object, null} [obj]        The object containing the post fade-in function (<b>optional</b>)
 * @param   {String} [funcname]         Function to be called after the element has faded in (<b>optional</b>)
 * @param   {Array} [params]            Parameters to be passed to the function (<b>optional</b>)
 */
function fadeIn(element, time, max_opacity, obj, funcname, params)
{
    if (typeof element != 'object') {
        element = document.getElementById(element);
    }
    max_opacity = max_opacity == null ? 100 : max_opacity;
    params      = params == null ? new Array() : params;

    var opacity = 0;
    var steps   = 20;   // Fade-in steps per second

    function _fade()
    {
        if (opacity > max_opacity) {
            if (obj != null) {
                obj[funcname].apply(obj, params);
            } else if (funcname != null) {
                var fp = eval(funcname);   // Get function pointer for given function name
                fp.apply(null, params);
            }
            return;
        }
        opacity += max_opacity / (time / 1000) / steps;
        setOpacity(element, opacity);
        setTimeout(_fade, 1000 / steps);
    }

    setOpacity(element, opacity);
    _fade();
}

/**
 * Lay a semi-transparent DIV over the document
 *
 * @param   {String} [color=#000000]    The color of the overlay DIV
 * @param   {Number} [opacity=70]       The opacity of the DIV
 * @param   {Number} [zIndex=100]       The z-index value of the DIV
 */
function shadeBackground(color, opacity, zIndex)
{
    color   = color == null ? '#000000' : color;
    opacity = opacity == null ? 70 : opacity;
    zIndex  = zIndex == null ? 100 : zIndex;

    var div = document.getElementById('div_shader');
    if (!div) {
        div = document.createElement('div');
        div.setAttribute('id', 'div_shader');
        document.body.appendChild(div);
    }

    var doc_dim = getDocumentDimensions();

    // Hide all select boxes in IE6
    if (navigator.userAgent.indexOf('MSIE 6.0') != -1
        && navigator.userAgent.search(/MSIE [7-9]\.0/) == -1)
    {
        var selects = document.getElementsByTagName('SELECT');
        for (var i = 0; i < selects.length; i++) {
            selects[i].style.visibility = 'hidden';
        }
    }

    setOpacity(div, opacity);

    div.style.display         = '';
    div.style.position        = 'absolute';
    div.style.zIndex          = zIndex;
    div.style.backgroundColor = color;
    div.style.left            = '0';
    div.style.top             = '0';
    div.style.width           = doc_dim.scrollWidth + 'px';
	if (doc_dim.scrollHeight > doc_dim.height) {
	    div.style.height = doc_dim.scrollHeight + 'px';
	} else if (navigator.userAgent.indexOf('MSIE') != -1) {
    	div.style.height = doc_dim.height + 'px';
	} else {
    	div.style.height = '100%';
	}
	if (navigator.userAgent.indexOf('MSIE') == -1)
	{
		div.style.height = doc_dim.scrollHeight + 'px';
	}
}

/**
 * Remove the semi-transparent shader DIV
 */
function unshadeBackground()
{
    removeElementById('div_shader');

    // Unhide all select boxes in IE6
    if (navigator.userAgent.indexOf('MSIE 6.0') != -1
        && navigator.userAgent.search(/MSIE [7-9]\.0/) == -1)
    {
        var selects = document.getElementsByTagName('SELECT');
        for (var i = 0; i < selects.length; i++) {
            selects[i].style.visibility = 'visible';
        }
    }
}

/**
* Diese Funktion prüft ob ein key eines Array's existiert
*
* @params mixed needle		Dies ist der zu suchende key
* @params array Haystack	Dies ist das zu durchsuchende Array
* @return Boolean			Es wird true zurückgegeben wenn der Wert vorhanden ist.
*/
function array_key_exists(needle, Haystack)
{
	if(typeof(Haystack[needle]) == "undefined")
	{
		return false;
	}
	return true;
}

/**
 * Trim leading whitespace characters
 *
 * @return  {String}                    The trimmed string
 */
String.prototype.ltrim = function ()
{
    return this.replace(/^\s+/, '');
};

/**
 * Trim trailing whitespace characters
 *
 * @return  {String}                    The trimmed string
 */
String.prototype.rtrim = function ()
{
    return this.replace(/\s+$/, '');
};

/**
 * Trim leading and trailing whitespace characters
 *
 * @return  {String}                    The trimmed string
 */
String.prototype.trim = function ()
{
    return this.ltrim().rtrim();
};
