function Request(url) {
    var self = this;
    self.url = url;
    self.objects = new Array();
    self.req_pending = false;
    self.req_time = 0;
    self.req_timer = 0;
    self.packer_name = false;
    self.callback_func = false;

    if( typeof XMLHttpRequest == "undefined" ) XMLHttpRequest = function() {
        try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch(e) {}
        try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch(e) {}
        try { return new ActiveXObject("Msxml2.XMLHTTP") } catch(e) {}
        try { return new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {}
        throw new Error( "This browser does not support XMLHttpRequest. Thus, no wBids for you, mate!" );
    };

    self.http = new XMLHttpRequest();

    self.addObject = function(obj)
    {
        self.objects[obj.getId()] = obj;
    }
    
    self.clearObjects = function()
    {
        delete self.objects;
        self.objects = new Array();
    }

    /* set callback function */
    self.setCallback = function(func)
    {
        self.callback_func = func;
    }

    /* set packer function _NAME_ */
    self.setPacker = function(name)
    {
        self.packer_name = name;
    }

    self.addParams = function(url, params)
    {
        var size = 0;
        for(var k in params) {
            var v = params[k];
            if(size == 0) url += '?';
            else url += '&';

            if(typeof v == 'object') {
                for(var k2 in v) {
                    var v2 = v[k2];
                    url += encodeURIComponent(k)+'[]='+encodeURIComponent(v2);
                }
            } else if(typeof v == 'string' || typeof v == 'number') {
                url += encodeURIComponent(k)+'='+encodeURIComponent(v);
            } else if(typeof v == 'undefined') {
                url += encodeURIComponent(k);
            }
            size++;
        }
        return url;
    }

    self.run = function(is_single, params)
    {
        if(typeof is_single == 'undefined')
            is_single = false;
        if(typeof params == 'undefined')
            params = true;

        /* if we are in the middle of a request, make a note we should send when this finishes */
        if(self.req_time) {
            self.req_pending = true;
            dprint('Request: tried to send a new request when old is still waiting, noted');
            return;
        }

        /* ask for data! */
        var urlParams = new Array();

        /* if we have no packer, we send every time we're run() */
        if(typeof self.packer_name == 'string') {
            var doQuery = false;
            for(var i in self.objects) {
                var obj = self.objects[i];


                /* TODO: make packer really do something else than return true/false */
                var packData = obj[self.packer_name]();

                /* if the packer just returns a boolean true, we send the query even without parameters */
                if(packData == true) {
                    doQuery = true;
                } else if(typeof packData == 'object') {
                    doQuery = true;
                    for(var k in packData) {
                        var v = packData[k];
                        /* only consider values that are strings */
                        if(typeof k == 'string' && (typeof v == 'string' || typeof v == 'number')) {
                            if(typeof urlParams[k] == 'string') {
                                /* if the key already exists, make it an array */
                                var tmp = urlParams[k];
                                urlParams[k] = new Array();
                                urlParams[k].push(tmp);
                                urlParams[k].push(v);
                            } else if(typeof urlParams[k] == 'object') {
                                /* if the value is an array, push a new value */
                                urlParams[k].push(v);
                            } else {
                                urlParams[k] = v;
                            }
                        }
                    }
                }
            }

            /* no one wants to send a request, screw it then */
            if(!doQuery)
                return;
        }


        /* add 'required' random parameter */
        urlParams[Math.floor(Math.random()*10000000)] = undefined;

        self.req_time = WBIDS.time();
        self.req_pending = false;
        self.http.open('GET', self.addParams(self.url, urlParams), true);
        self.http.onreadystatechange = self.callback;
        self.http.send(null);

        delete urlParams;
    }

    self.callback = function() {
        if(self.http.readyState == 4) {
            if (self.http.status == 200) {

                if(self.http.responseText.length == 0) {
                    self.req_time = false;
                    dprint("Warning: HTTP response was empty!");
                    return;
                }

                if(typeof self.callback_func == 'function')
                    self.callback_func(self.http.responseText, WBIDS.time() - self.req_time);
                else
                    dprint("Warning: HTTP reply discarded for url "+self.url+": callback function not defined!");

                self.req_time = false;
            } else {
                if(self.http.status != 404) {
                    dprint('Request: failed! [status: ' + self.http.status + '] and resending');
                    self.run();
                } else {
                    dprint('Request: failed, got 404, doing nothing');
                }
                return;
            }

            if(self.req_pending) {
                dprint('Request: sending a pending request immediately');
                self.run();
            }
        }
    }
}

