if (! Array.prototype.map)
{
    Array.prototype.map = function(fun /*, thisp*/)
    {
        var len = this.length;
        if (typeof fun != "function")
        {
            throw new TypeError();
        }

        var res = new Array(len);
        var thisp = arguments[1];
        for (var i = 0; i < len; i++)
        {
            if (i in this)
            {
                res[i] = fun.call(thisp, this[i], i, this);
            }
        }

        return res;
    };
}
if (! String.prototype.trim)
{
    String.prototype.trim = function()
    {
        return this.replace(/^\s+|\s+$/g, '');
    };
}
if (! String.prototype.normalize)
{
    String.prototype.normalize = function()
    {
        return this.trim().replace(/\s+/, ' ');
    };
}

if (! Date.prototype.setISO8601)
{
    Date.prototype.setISO8601 = function (string) {
        var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
                     "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
                     "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
        var d = string.match(new RegExp(regexp));

        var offset = 0;
        var date = new Date(d[1], 0, 1);

        if (d[3]) { date.setMonth(parseInt(d[3]) - 1); }
        if (d[5]) { date.setDate(parseInt(d[5])); }
        if (d[7]) { date.setHours(parseInt(d[7])); }
        if (d[8]) { date.setMinutes(parseInt(d[8])); }
        if (d[10]) { date.setSeconds(parseInt(d[10])); }
        if (d[12]) { date.setMilliseconds(parseFloat("0." + d[12]) * 1000); }
        if (d[14]) {
            offset = (parseFloat(d[16]) * 60) + parseFloat(d[17]);
            offset *= ((d[15] == '-') ? 1 : -1);
        }

        offset -= date.getTimezoneOffset();
        time = (date.getTime() + (offset * 60 * 1000));
        this.setTime(parseFloat(time));
    }
}

// http://www.van-steenbeek.net/?q=explorer_domparser_parsefromstring
if(typeof(DOMParser) == 'undefined')
{
    DOMParser = function() {}

    DOMParser.prototype.parseFromString = function(str, contentType) {
        if (typeof(ActiveXObject) != 'undefined') {
            var xmldata = new ActiveXObject('Microsoft.XMLDOM');
            xmldata.async = false;
            xmldata.loadXML(str);
            return xmldata;
        }
        else if (typeof(XMLHttpRequest) != 'undefined') {
            var xmldata = new XMLHttpRequest;

            if (!contentType) {
                contentType = 'application/xml';
            }
            xmldata.open('GET', 'data:' + contentType + ';charset=utf-8,' + encodeURIComponent(str), false);
            if (xmldata.overrideMimeType) {
                xmldata.overrideMimeType(contentType);
            }
            xmldata.send(null);
            return xmldata.responseXML;
        }
    }
}

function findElements(xml, ns, localName, qualifier)
{
    if (xml.getElementsByTagNameNS)
    {
        return xml.getElementsByTagNameNS(ns, localName);
    }
    var prefix = (qualifier ? (qualifier + ':') : '');
    return xml.getElementsByTagName(prefix + localName);
}
function findFirstElement(xml, ns, localName, qualifier)
{
    var el = findElements(xml, ns, localName, qualifier);
    if (el.length > 0)
    {
        return el[0];
    }
    return null;
}
function setElementText(el, txt)
{
    if (el.textContent !== undefined)
    {
        el.textContent = txt;
    }
    else
    {
        el.innerText = txt;
    }
}

function getXMLHttpRequest()
{
    if (window.XMLHttpRequest)
    {
        return new XMLHttpRequest();
    }
    else if (window.ActiveXObject)
    {
        return new ActiveXObject("Microsoft.XMLHTTP");
    }
    return null;
}

// from https://blueprints.dev.java.net/ajax-faq.html
// with a few additions
// callback is called with the XML from the result on success
// errorCallback is called with the entire request object on failure
// the callback and errorCallback are optional
function AJAXInteraction(url, callback, errorCallback, raw)
{
    var req = getXMLHttpRequest();
    req.onreadystatechange = processRequest;

    function processRequest()
    {
        if (req.readyState == 4)
        {
            if (req.status == 200)
            {
                if (callback)
                {
                    callback(raw ? req : req.responseXML);
                }
            }
            else
            {
                if (errorCallback)
                {
                    errorCallback(req);
                }
            }
        }
    }

    this.send = function(body, type)
    {
        req.open((body ? "POST" : (callback ? "GET" : "HEAD")), url, true);
        if (type)
        {
            req.setRequestHeader("Content-Type", type);
        }
        else
        {
            req.setRequestHeader("Content-Type",
                                 "application/x-www-form-urlencoded");
        }
        req.send(body);
    }
}
