﻿/// <reference path="jquery-1.3.2-vsdoc.js />
(function() {
    var $ = jQuery;

    var placeHolder = $("<div />");
    placeHolder.css("display", "none");

    var updatePanelhandler = function(context, options) {
        return context.updatePanel(options.url, options.data, options.httpMethod, options.dataType);
    }


    $.ajaxPanel = {}
    $.ajaxPanel.settings = {}

    var ajaxPanel = function(options, context) {
    
        //
        $.extend(this, $.ajaxPanel.settings);
        $.extend(this, options);

        this.context = context;
        this.updatePanel = context.updatePanel = function(url, data, httpMethod, dataType, callback) {
            url = url || me.url;
            data = data || me.data;
            dataType = dataType || me.dataType;
            httpMethod = httpMethod || me.httpMethod;

            _ajaxRequest({ url: url, data: data, method: httpMethod, callback: callback });
        };

        var me = this;

        var _init = function() {
            _initComponent();
            _bindEvents();
            _initPartialView();
        };

        var _initComponent = function() {
            if (me.selector == "") {
                me.selector = context.selector;
            } else {
                this.context = context = $(me.selector);
            }
        };

        var _bindEvents = function() {
            //bind event and handler

            var subs = me.subscribers;
            if (subs) {
                for (var n in subs) {
                    var fn = subs[n];
                    if (fn) {
                        if (typeof (fn) == "function") {
                            context.subscribe(n, fn);
                        }
                        if (typeof (fn) == "object") {
                            var obj = fn;
                            context.subscribe(n, updatePanelhandler, [me, obj]);
                        }
                    }
                    else {
                        context.subscribe(n, me.updatePanel);
                    }
                }
            }
        };

        var _unbindEvents = function() {
            var subs = me.subscribers;
            if (subs) {
                for (var n in subs) {
                    var fn = subs[n];
                    if (fn) {
                        if (typeof (fn) == "function") {
                            context.unsubscribe(n, fn, this);
                        }
                        if (typeof (fn) == "object") {
                            context.unsubscribe(n, updatePanelhandler, this);
                        }
                    }
                    else {
                        context.unsubscribe(n, me.updatePanel, this);
                    }
                }
            }
        };

        var _initPartialView = function() {

            var acts = $(me.actionSelector, context);

            acts.each(function() {
                if (this.tagName === "FORM") {
                    $(this).submit(function(evt) {
                        return _handleAction.call(this, evt, context);
                    });
                } else {
                    $(this).click(function(evt) {
                        return _handleAction.call(this, evt, context);
                    });
                }
            });
        }

        var _appendTicksToUrl = function(url) {
            var retval = "";
            var d = new Date();
            if (url.indexOf("?") == -1) {
                retval = url + "?t=" + d.valueOf();
            } else {
                retval = url + "&t=" + d.valueOf();
            }
            return retval;
        }

        var _ajaxRequest = function(options) {
            // fire beforeUpdate
            //$.ajaxPanel.beforeUpdate && $.ajaxPanel.beforeUpdate(context);
            me.beforeUpdate && me.beforeUpdate();
            if (me.replaceMode) {
                me.unbindEvents();
            }

            var url = _appendTicksToUrl(options.url);
            var callback = options.callback;

            $.ajax({
                url: url,
                beforeSend: function(req) {
                    //append the httpRequest header to indicate the partial request.
                    req.setRequestHeader(me.headerName, me.targetName);
                },
                type: options.method,
                data: options.data,
                success: function(result) {
                    me.updateUI(result);

                    //$.ajaxPanel.afterUpdate && $.ajaxPanel.afterUpdate(context);
                    me.afterUpdate && me.afterUpdate();
                    callback && callback();
                    _initPartialView();
                },
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    me.error && me.error(XMLHttpRequest, textStatus, errorThrown);
                }
            });
        }

        var _handleAction = function(evt, currReq) {

            // get orignal event object. jq's event object is evt.originalEvent.orig;
            var origEvt = evt.originalEvent || evt;
            // if it was handled by other event handler, return
            if (origEvt.cancelBubble) { return _cancelEvt(origEvt); }
            // if it's one popup window, return and execute the default operatioin.
            if (this.target == '_blank' || this.target == '_top') return;
            // get the ajaxRequest parameters.
            var postData, url, method;
            if (this.tagName === "FORM") {
                url = this.action;

                var data = _formData(this);
                method = this.method || "GET";
                method = method.toUpperCase();
                if (method === "GET") {
                    url = _buildGetUrl(url, data);
                    data = undefined;
                } else {
                    postData = data;
                }
            } else {
                url = $.trim(this.href || this.getAttribute("href") || "");
                method = "GET";
            }
            // the url is not available, return .
            if (!url || url === "#") return;
            _ajaxRequest({ url: url, method: method, data: postData });
            return _cancelEvt(origEvt);
        }


        var _cancelEvt = function(evt) {
            if (evt.preventDefault) evt.preventDefault();
            if (evt.stopPropagation) evt.stopPropagation();
            evt.cancelBubble = true;
            evt.returnValue = false;
            return false;
        }

        //Convert the form fields to json object;
        var _formData = function(form) {
            var data = {}, j = form.elements.length;
            for (var i = 0; i < j; i++) {
                var el = form.elements[i];
                if (!el.name) continue;
                if (el.type === "checkbox" || el.type === "radio") {
                    if (!el.checked) continue;
                }
                if (data[el.name] === undefined)
                    data[el.name] = el.value;
                else
                    data[el.name] = data[el.name] + "," + el.value;
            }
            return data;
        }

        //Compose the get url 
        var _buildGetUrl = function(url, data) {
            url = $.trim(url) || document.location.href;
            if (!data) return url;
            url += url.indexOf("?") == -1 ? "?" + $.param(data) : "&" + $.param(data);
            return url;
        }


        //Check the element if it is in document.
        var _isInDoc = function(el) {
            var doc = el.ownerDocument;
            while (el) {
                if (el === document.body || el === doc) return true;
                el = el.parentNode;
            }
            return false;
        }

        _init();
    }

    ajaxPanel.prototype = {
        httpMethod: "GET",
        dataType: "HTML",
        headerName: "ajax-call",
        targetName: "content",
        replaceMode: false,
        url: "",
        selector: "",
        context: null,
        actionSelector: "",
        data: {},
        subscribers: {},
        beforeUpdate: function() { },
        afterUpdate: function() { },
        updateUI: function(result) {
            var target = this.context;
            if (this.replaceMode) {
                target.replaceWith(placeHolder);
                placeHolder.after(result).remove();
            }
            else {
                // inner html 
                target.html(result);
            }
        },
        subscribe: function(eventName, eventHandler, constArgs) {
            this.context.subscribe(eventName, eventHandler, constArgs);
            return this;
        },
        unsubscribe: function(eventName, eventHandler) {
            this.context.unsubscribe(eventName, eventHandler);
            return this;
        },
        error: function() { }
    }

    $.fn.ajaxPanel = function(options) {
        var _ajaxPanel = new ajaxPanel(options, this);
        return _ajaxPanel;
    }

})();


