﻿/**
 * Ajax.Request.abort
 * extend the prototype.js Ajax.Request object so that it supports an abort method
 */
Ajax.Request.prototype.abort = function() {
    
    // prevent and state change callbacks from being issued
    this.transport.onreadystatechange = Prototype.emptyFunction;
    // abort the XHR
    this.transport.abort();
    // update the request counter
    Ajax.activeRequestCount--;
    if (Ajax.activeRequestCount < 0) {
        Ajax.activeRequestCount = 0;
    }
};

Array.prototype.peek = function() {
    return this[this.length-1];
};

var fanlebrity_ajaxrequest = function(){ return this;};

fanlebrity_ajaxrequest.prototype = {

    stacks: new Array(),
    urls: new Array(),

    req: function(o) {
        if (!!o.id) o.id = $(o.id);
        if (o.tempId == undefined) o.tempId = o.id
        else o.tempId = $(o.tempId);
        if (o.sourceChild == undefined) o.sourceChild = false;
        if (o.destChild == undefined) o.destChild = false;
        o.resume = (o.resume == false) ? false : true;
        o.method = o.method || "POST";
        o.params = o.params || '';
        o.useCache = (o.useCache == null) ? true : o.useCache;
        o.runActions = (o.runActions == null) ? true : o.runActions;

        o.started = false;
        o.done = false;

        var qObj = this.makeQueryObject(o.url, o.method, o.params);
        o.url = qObj.url;
        o.params = qObj.params;
        o.urlKey = qObj.urlKey;

        if (o.stackKey) this.setupStack(o);

        o.urlObj = this.setupUrlObj(o);

        this.startReq(o);
    },

    setupStack: function(o) {
        var pop;

        o.stack = this.stacks[o.stackKey];
        if (!o.stack)
            o.stack = this.stacks[o.stackKey] = new Array();

        if (pop = o.stack.pop()) {
            //if (pop.done) alert('wtf');
            if (o.urlKey != pop.urlKey) {
                pop.urlObj.req.abort();
                pop.urlObj.req = null;
                pop.started = false;
            }
            if (pop.resume) o.stack.push(pop);
        }

        o.stack.push(o);
        var x = 1;
    },

    setupUrlObj: function(o) {
        var urlObj;

        urlObj = this.urls[o.urlKey];
        if (!urlObj) {
            urlObj = this.urls[o.urlKey] = { handlers: this.emptyHandler(), cache: false };
        }

        return urlObj;
    },

    setupHandlers: function(o) {
        if (!!o.override) {
            if (o != o.urlObj.handlers[o.override]) {
                if (o.urlObj.handlers[o.override]) o.urlObj.handlers[o.override].done = true;
                o.urlObj.handlers[o.override] = o;
            }
        } else
            o.urlObj.handlers[''].push(o);
    },

    startReq: function(o) {
        //dbg('start ' + o.urlKey);
        var _this = this;

        if (o.started) return;
        o.started = true;

        if (o.tempMsg && o.tempId) o.tempId.innerHTML = o.tempMsg;

        if (o.urlObj.cache && o.useCache) {
            o.response = o.urlObj.cache;
            this.processReq(o);
        } else {
            this.setupHandlers(o);

            if (!o.urlObj.req) {
                o.urlObj.req = new Ajax.Request(o.url,
                { method: o.method
                , parameters: o.params
                , onSuccess: function(r) { _this.onSuccess(r, o.urlObj); }
                , onFailure: this.onFail
                });
            }
        }
    },

    onSuccess: function(r, urlObj) {
        //dbg('onSucc ' + r.responseText);
        var _this = this;

        urlObj.req = null;

        var re = this.processResponse(r);

        urlObj.cache = re;

        function p(h) {
            h.response = re;
            _this.processReq(h);
        }

        Object.keys(urlObj.handlers).each(
            function(k) {
                if (Object.isArray(urlObj.handlers[k]))
                    urlObj.handlers[k].each(p);
                else
                    p(urlObj.handlers[k]);
            }
        )

        urlObj.handlers = this.emptyHandler();
    },

    processReq: function(o) {
        var peek;

        if (o.done) return;
        o.done = true;

        if (o.tempMsg && o.tempId) o.tempId.innerHTML = '';

        if (!!o.runActions) {
            if (o.id) this.updateContent(o);
            if (o.response.js) eval(o.response.js);
        } else {
            var _this = this;
            o.updateContent = function() { _this.updateContent(o); };
            o.evalJS = function() { eval(o.response.js); };
        }

        if (o.onSuccess) o.onSuccess(o);

        if (o.stack) {
            while ((peek = o.stack.peek()) && (peek.started || peek.done))
                o.stack.pop();

            if (peek = o.stack.peek())
                this.startReq(peek);
        }
    },

    processResponse: function(r) {
        var retVal;

        if (json = r.responseJSON) {
            retVal = { html: json.html, data: json.data, js: json.js };
        } else {
            retVal = { html: r.responseText };
        }

        retVal.response = r;

        return retVal;
    },

    onFail: function(e) {
        alert('ajax fail');
        alert($(e).toJSON());
    },

    makeQueryObject: function(url, method, params) {
        var r = { url: '', urlKey: '', params: '' };

        var i, sParams1, sParams2;

        var i = url.indexOf('?');
        r.url = url.substring(0, i);
        sParams1 = url.substring(i + 1);

        if (Object.isString(params))
            sParams2 = params;
        else {
            sParams2 = this.obj2query(params);
        }

        if (sParams1 && sParams2) sParams1 += "&"
        r.params = sParams1 + sParams2;

        r.urlKey = r.url + "?" + r.params;

        if (method.toLowerCase() == "post") {
            r.url = url;
            r.params = params;
        }

        return r;
    },

    updateContent: function(o) {

        if (!o.sourceChild && o.destChild) {
            o.id.update(o.response.html);
            return;
        }

        var source = new Element('div');

        source.innerHTML = o.response.html;

        if (o.sourceChild && o.destChild) {
            o.id.update(source.firstDescendant().innerHTML);
            return;
        }

        if (!o.sourceChild && !o.destChild) {
            o.id.replace(source.firstDescendant());
            return;
        }

        alert('oops');

    },

    obj2query: function(obj, forPHP, parentObject) {
        if (typeof obj != 'object') return '';

        if (arguments.length == 1)
            forPHP = /\.php$/.test(document.location.href);

        var rv = '';
        for (var prop in obj) if (obj.hasOwnProperty(prop)) {

            var qname = parentObject
             ? parentObject + '.' + prop
             : prop;

            // Expand Arrays
            if (obj[prop] instanceof Array)
                for (var i = 0; i < obj[prop].length; i++)
                if (typeof obj[prop][i] == 'object')
                rv += '&' + obj2query(obj[prop][i], forPHP, qname);
            else
                rv += '&' + encodeURIComponent(qname) + (forPHP ? '[]' : '')
					    + '=' + encodeURIComponent(obj[prop][i]);

            // Expand Dates
            else if (obj[prop] instanceof Date)
                rv += '&' + encodeURIComponent(qname) + '=' + obj[prop].getTime();

            // Expand Objects
            else if (obj[prop] instanceof Object)
            // If they're String() or Number() etc
                if (obj.toString && obj.toString !== Object.prototype.toString)
                rv += '&' + encodeURIComponent(qname) + '=' + encodeURIComponent(obj[prop].toString());
            // Otherwise, we want the raw properties
            else
                rv += '&' + obj2query(obj[prop], forPHP, qname);

            // Output non-object
            else
                rv += '&' + encodeURIComponent(qname) + '=' + encodeURIComponent(obj[prop]);

        }
        return rv.replace(/^&/, '');
    },

    emptyHandler: function() { return { '': [] }; }


}



var _ajaxr = new fanlebrity_ajaxrequest();
