function EricAjaxLibrary (template, username, password, url) {
    if (!template) throw "Missing template parameter for EricAjaxLibrary().";

    this.template = template;
    this.username = username;
    this.password = password;
    this.url = url ? url : "/include/run-macro.php";

    this.requests = new Array();
}

EricAjaxLibrary.prototype.runMacro = function (macro, params, callback) {
    if (!macro) throw "No macro specifed!";
    if (params && typeof(params) != "object") throw "The macro's parameters must be specified as an object (hash).";
    if (callback && typeof(callback) != "function") throw "The 'callback' parameter must be a function reference.";

    // Create a new XMLHttpRequest object.
    var req = false;
    if (window.XMLHttpRequest) {
        try { req = new XMLHttpRequest(); }
        catch (e) { req = false; }
    }
    else if (window.ActiveXObject) {
        try { req = new ActiveXObject("Msxml2.XMLHTTP"); }
        catch (e) {
            try { req = new ActiveXObject("Microsoft.XMLHTTP"); }
            catch (e) { throw "Failed to create XMLHTTPRequest object!"; }
        }
    }

    // It would be nice if these next two lines could be atomic. Oh well,
    // just cross your fingers.
    this.requests.push(req);
    var reqNum = this.requests.length - 1;

    // Set up the POST query string.
    var q = "__t="+escape(this.template)+"&";
    q += "__m="+encodeURIComponent(macro)+"&";
    if (this.username) q += "__u="+encodeURIComponent(this.username)+"&";
    if (this.password) q += "__p="+encodeURIComponent(this.password)+"&";
    for (var i in params) {
        q += encodeURIComponent(i)+"="+encodeURIComponent(params[i])+"&";
    }

    // Set up the callback. Yay for javascript closures!
    var me = this;
    var callbackClosure = function () {
        if (req.readyState == 4) {
            if (req.status == 200) {
                eval("var _response = " + req.responseText + ";");
                if (_response.ericAjaxResponseHeader.error)
                    throw _response.ericAjaxResponseHeader.error;
                if (callback) callback(_response.ericAjaxResult);
            }
            else {
                throw "Error executing macro: " + req.statusText;
            }

            // Remove request reference so it can be garbage collected.
            me.requests[reqNum] = null;

            // Break circular references for IE's crappy garbage collector.
            delete req.onreadystatechange;
            req = null;
        }
    };

    // Do the request!
    req.onreadystatechange = callbackClosure;
    req.open("POST", this.url, true);
    req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    req.send(q);
}