/* [[Benutzer:D/monobook]] ::: API ::: [[Benutzer:D/monobook/api.js]] */ /*
 */

//======================================================================
//## util/lang.js 

//------------------------------------------------------------------------------
//## Object

// NOTE: these do _not_ break for (foo in bar)

/** Object helper functions */
var Objects = {
    /** create an Object from a prototype */
    object: function(obj) {
        function F() {}
        F.prototype = obj;
        return new F();
    },
    
    /** copies an Object's properties into an new Object */
    copyOf: function(obj) {
        var out = {};
        for (var key in obj)
                if (obj.hasOwnProperty(key))    
                        out[key] = obj[key];
        return out;
    },
    
    /** copies an object's properties into another object */
    copySlots: function(source, target) {
        for (var key in source)
                if (source.hasOwnProperty(key)) 
                        target[key] = source[key];
    },
    
    /** an object's own property names as an Array */
    ownSlots: function(obj) {
        var out = [];
        for (key in obj)
                if (obj.hasOwnProperty(key))
                        out.push(obj);
    },
    
    /** returns an object's slots as an Array of Pairs */
    toPairs: function(obj) {
        var out = [];
        for (var key in obj)
                if (obj.hasOwnProperty(key))
                        out.push([key, obj[key]]);
        return out;
    },
    
    /** creates an Object from an Array of key/value pairs, the last Pair for a key wins */
    fromPairs: function(pairs) {
        var out = {};
        for (var i=0; i]*>/, ""));
    },
    
    /** serialize an XML (e4x) to a String */
    unparseE4X: function(xml) {
        return xml.toXMLString();
    },
    
    //------------------------------------------------------------------------------
    //## escaping
    
    /** escapes XML metacharacters */
    encode: function(str) { 
        return str.replace(/&/g,    '&')
                    .replace(//g,  '>');
    },
    
    /** escapes XML metacharacters including double quotes */
    encodeDQ: function(str) {
        return str.replace(/&/g,    '&')
                    .replace(//g,  '>')
                    .replace(/\"/g, '"');
    },
    
    /** escapes XML metacharacters including single quotes */
    encodeSQ: function(str) {
        return str.replace(/&/g,    '&')
                    .replace(//g,  '>')
                    .replace(/\'/g, ''');
    },
    
    /** decodes results of encode, encodeDQ and encodeSQ */
    decode: function(code) {
        return code.replace(/"/g,   '"')
                    .replace(/&apos/g,  "'")
                    .replace(/>/g,   ">")
                    .replace(/</g,   "<")
                    .replace(/&/g,  "&");
    }//,
};

//======================================================================
//## util/Loc.js 

/**
 * tries to behave similar to a Location object
 * protocol includes everything before the //
 * host     is the plain hostname
 * port     is a number or null
 * pathname includes the first slash or is null
 * hash     includes the leading # or is null
 * search   includes the leading ? or is null
 */
function Loc(urlStr) {
    var m   = this.parser(urlStr);
    if (!m) throw "cannot parse URL: " + urlStr;
    this.local      = !m[1];
    this.protocol   = m[2] ? m[2] : null;                           // http:
    this.host       = m[3] ? m[3] : null;                           // de.wikipedia.org
    this.port       = m[4] ? parseInt(m[4].substring(1)) : null;    // 80
    this.pathname   = m[5] ? m[5] : "";                             // /wiki/Test
    this.hash       = m[6] ? m[6] : "";                             // #Industry
    this.search     = m[7] ? m[7] : "";                             // ?action=edit
}
Loc.prototype = {
    /** matches a global or local URL */
    parser: /((.+?)\/\/([^:\/]+)(:[0-9]+)?)?([^#?]+)?(#[^?]*)?(\?.*)?/,

    /** returns the href which is the only usable string representationn of an URL */
    toString: function() {
        return this.hostPart() + this.pathPart();
    },

    /** returns everything before the pathPart */
    hostPart: function() {
        if (this.local) return "";
        return this.protocol + "//" + this.host
            + (this.port ? ":" + this.port  : "");
    },

    /**  returns everything local to the server */
    pathPart: function() {
        return this.pathname + this.hash + this.search;
    },

    /** converts the searchstring into an Array of name/value-pairs */
    args: function() {
        if (!this.search)   return [];
        var out     = [];
        var split   = this.search.substring(1).split("&");
        for (var i=0; i= elements.length)       return null;
        return elements[index];
    },

    /** find the next element from el which has a given nodeName or is non-text */
    nextElement: function(el, nodeName) {
        if (nodeName)   nodeName    = nodeName.toUpperCase();
        for (;;) {
            el  = el.nextSibling;   if (!el)    return null;
            if (nodeName)   { if (el.nodeName.toUpperCase() === nodeName)   return el; }
            else            { if (el.nodeName.toUpperCase() !== "#TEXT")    return el; }
        }
    },

    /** find the previous element from el which has a given nodeName or is non-text */
    previousElement: function(el, nodeName) {
        if (nodeName)   nodeName    = nodeName.toUpperCase();
        for (;;) {
            el  = el.previousSibling;   if (!el)    return null;
            if (nodeName)   { if (el.nodeName.toUpperCase() === nodeName)   return el; }
            else            { if (el.nodeName.toUpperCase() !== "#TEXT")    return el; }
        }
    },

    /** whether an ancestor contains an element */
    contains: function(ancestor, element) {
        for (;;) {
            if (element === ancestor)   return true;    
            if (element === null)       return false;
            element = element.parentNode;
        }
    },
    
    //------------------------------------------------------------------------------
    //## add

    /** inserts text, a node or an Array of these before a target node */
    pasteBefore: function(target, additum) {
        if (additum.constructor !== Array)  additum = [ additum ];
        var parent  = target.parentNode;
        for (var i=0; i= 200 && status < 300) {
                if (args.successFunc)       args.successFunc(client, client.responseText);
            }
            else if (status >= 300 && status < 400) {
                // TODO location-header?
                if (args.intermediateFunc)  args.intermediateFunc(client);
            }
            else if (status >= 400 && status < 500) {
                if (args.failureFunc)       args.failureFunc(client);
            }
            else if (status >= 500 && status < 600) {
                if (args.errorFunc)         args.errorFunc(client);
            }
            
            if (args.completeFunc)  args.completeFunc(client);
            if (status < 200 || status >= 300) {
                if (args.noSuccessFunc)     args.noSuccessFunc(client);
            }
        };
        
        
        // init headers
        if (args.bodyParams) {
            var contentType = "application/x-www-form-urlencoded";
            if (args.charset)   contentType += "; charset=" + args.charset;
            client.setRequestHeader("Content-Type", contentType);
        }
        if (args.headers) {
            for (var key in args.headers) {
                client.setRequestHeader(key, args.headers[keys]);
            }
        }
        
        // init body
        var body;
        if (args.bodyParams) {
            body    = this.encodeFormArgs(args.bodyParams);
        }
        else {
            body    = args.body || null;
        }
        
        // send
        if (args.timeout) {
            client.timer    = setTimeout(client.abort.bind(client), args.timeout);
        }
        client.send(body);
        
        
    },
    
    //------------------------------------------------------------------------------
    //## private
    
    /** 
     * url-encode arguments
     * args may be an Array of Pair of String or a Map from String to String 
     */
    encodeUrlArgs: function(args) {
        if (args.constructor !== Array) args    = this.hashToPairs(args);
        return this.encodeArgPairs(args, encodeURIComponent);
    },
    
    /**
     * encode arguments into application/x-www-form-urlencoded 
     * args may be an Array of Pair of String or a Map from String to String 
     */
    encodeFormArgs: function(args) {
        if (args.constructor !== Array) args    = this.hashToPairs(args);
        return this.encodeArgPairs(args, this.encodeFormValue);
    },
    
    /** compile an Array of Pairs of Strings into the &name=value format */
    encodeArgPairs: function(args, encodeFunc) {
        var out = "";
        for (var i=0; i= 0
            && nameOrIdOrIndex < forms.length)  return forms[nameOrIdOrIndex];
            else                                return null;
        }
        for (var i=0; i
*/