﻿/// <reference path="jquery.intellisense.js" />
/// <reference path="jquery.json.js" />
/// <reference path="firebug/firebug.js" />
/*
* jQuery PageMethods Plugin v1.3.4
* Requires jQuery 1.2.6+
*
* This plugin allows you to call ASP.Net 3.5 Web Services 
* and Page Methods through the use of JSON objects.
* It uses internal dot net object json serialization.
*
* Copyright (c) 2008 Omer Duzyol - MagiClick Digital Solutions
* Licensed under MIT license.
*
* 2008-18-11
*
* History
* - methodBase.Call function added to track caller source
* - Syncronous error handling fixed.
* - negative signed date parsing problem fixed - /Date(-357361200000)/
* 
* Last update on 16.07.2009 
*/
$.Page = new function() {
    // Global events 
    this.Events = { onError: null, onBeforeCall: null, onFinally: null };
    /*  You can extend default types simply.
    For example extending String as a new type...
        
    $.extend($.Page.Types,{
    String: function(data){
    // some code
    return data;
    },
    });
        
    */
    // Supported .net data types
    this.Types = {
        Int32: function(data) {
            // Int32[] Array or Int32[] Dictionary
            if (data.constructor == Object || data.constructor == Array) {
                return data;
            }
            // Simple Int32 but presented in string
            else if (typeof data !== 'number') {
                try {
                    data = parseInt(data);
                    return (isNaN(data) ? null : data);
                } catch (e) { return null; }
            }
            return data;
        },
        DateTime: function(data) {
            if (typeof data === 'object') {
                return data.toJSON();
            }
            return data;
        }
    };

    // Base method for page methods
    this.methodBase = function(url, method, params) {

        function _evalJson(strJson, self, src) {
            var jsonCheck = null, ret = null, errThrown = false;
            try {
                jsonCheck = $.JSON.parse(strJson, function(key, value) {
                    var f1, f2;
                    if (typeof value === 'string') {
                        f1 = /^\/Date\((-{0,1}\d+)\)\/$/.exec(value);
                        if (f1)
                            return new Date(parseInt(f1[1]));
                        f2 = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                        if (f2)
                            return new Date(Date.UTC(+f2[1], +f2[2] - 1, +f2[3], +f2[4], +f2[5], +f2[6]));
                    }
                    return value;
                });
            } catch (e) { }
            if (jsonCheck != null && jsonCheck.d != null && jsonCheck.d.Succeed != null) {
                ret = jsonCheck.d;
            }

            if (jsonCheck != null && jsonCheck.d != null && jsonCheck.d.Exception != null && jsonCheck.d.Message != null) {
                errThrown = true;
                // raise an exception
                ret = new Error();
                ret.fileName = self.Url + '.cs';
                ret.lineNumber = 1;
                ret.name = 'Handled Webmethod Exception';
                ret.message = jsonCheck.d.Message;
                ret.description = jsonCheck.d.Exception;
                if (self.Events.onError)
                    self.Events.onError.apply(self, [ret, src]);
                else if ($.Page.Events.onError)
                    $.Page.Events.onError.apply(self, [ret, src]);
                else
                    throw ret;
            }

            if (self.Callback != null) {
                self.Callback(jsonCheck.d, src);
                return;
            } else {
                return jsonCheck.d;
            }
        }

        // if we have formatter in $.Page.Types cache, use formatter.
        function _fixFormat(data, format) {
            if (format in $.Page.Types)
                return $.Page.Types[format](data);
            else
                return data;
        }

        // convert given arguments into json request
        function _parseParams(arrArgs) {
            var objParams = $.extend({}, params);
            var parID = 0;
            for (oName in params) {
                var val = arrArgs[parID];
                if (val != null)
                    objParams[oName] = _fixFormat(val, params[oName]);
                else
                    objParams[oName] = null;
                parID++;
            }
            return $.JSON.stringify(objParams);
        }

        // core caller function for every method
        function _exec() {
            var src = this.sender || 'none';
            var self = arguments.callee;
            var urlText = self.Url + '/' + self.Method;
            var identifier = new Date().getTime();
            var requestedData = _parseParams(arguments);
            var ajaxOpts = {
                type: 'POST',
                url: urlText,
                data: requestedData,
                async: false,
                contentType: 'application/json; charset=utf-8',
                dataType: 'text',
                beforeSend: function(xhr) {
                    if (self.Events.onBeforeCall != null)
                        self.Events.onBeforeCall(xhr, urlText, requestedData, identifier, src);
                    else if ($.Page.Events.onBeforeCall != null)
                        $.Page.Events.onBeforeCall(xhr, urlText, requestedData, identifier, src);
                    xhr.setRequestHeader("X-Requested-With", "JQuery PageEvents");
                },
                error: function(xhr, textStatus, errorThrown) {
                    if (xhr.status == 500) {
                        var ret = new Error();
                        ret.fileName = self.Url + '.cs';
                        ret.lineNumber = 1;
                        var jerr = null;
                        try {
                            jerr = $.JSON.parse(xhr.responseText);
                        } catch (e) { }
                        if (jerr) {
                            ret.name = jerr.ExceptionType;
                            ret.message = jerr.Message;
                            ret.description = jerr.StackTrace;
                        } else {
                            var rex = /<!(?:--(?:[^-]*|-[^-]+)*--\s*)>/.exec(xhr.responseText);
                            if (rex) {
                                var strErr = rex[0].replace(/<!--/g, '').replace(/-->/g, '');
                                ret.name = /\[(.*?)\]:/.exec(strErr)[1];
                                ret.message = /\]:\s(.+)/.exec(strErr)[1];
                                ret.description = strErr;
                            } else {
                                ret.name = xhr.statusText;
                                ret.message = 'Internal server error.';
                                ret.description = xhr.responseText;
                            }
                        }

                        var selfErrorBlocked = true;
                        if ($.Page.Events.onError != null)
                            selfErrorBlocked = $.Page.Events.onError(ret, xhr, textStatus, errorThrown, src) || false;

                        if (self.Events.onError != null && selfErrorBlocked)
                            self.Events.onError(ret, xhr, textStatus, errorThrown, src);
                        else if (selfErrorBlocked)
                            throw ret;
                    }
                },
                complete: function(XMLHttpRequest, textStatus) {
                    if (self.Events.onFinally != null)
                        self.Events.onFinally(XMLHttpRequest, urlText, textStatus, identifier, src);
                    else if ($.Page.Events.onFinally != null)
                        $.Page.Events.onFinally(XMLHttpRequest, urlText, textStatus, identifier, src);
                }
            };
            //if an asynchronous call?
            if (self.Callback != null) {
                $.extend(ajaxOpts, { async: true, success: function(data) {
                    _evalJson(data, self, src);
                }
                });
                $.ajax(ajaxOpts);
            } else {
                var xhr = $.ajax(ajaxOpts);
                if (xhr.readyState == 4) {
                    if (jQuery.httpSuccess(xhr))
                        return _evalJson(jQuery.httpData(xhr, ajaxOpts.dataType), self, src);
                }
            }
            return;
        }
        var newMethod = $.extend(_exec, this);
        if (newMethod != null) {
            newMethod.Url = url;
            newMethod.Method = method;
            newMethod.Params = params;
            newMethod.Events = $.extend({}, $.Page.Events);
            newMethod.Call = function(sender) {
                var clone = $.extend(_exec, newMethod, { sender: sender });
                var args = new Array();
                if (arguments.length >= 1) {
                    $.each(arguments, function() { args.push(this); });
                    return clone.apply(this, args.slice(1));
                } else {
                    return clone.apply(this, args);
                }
            };
            return newMethod;
        }
    };
    this.methodBase.prototype.Url = null;
    this.methodBase.prototype.Method = null;
    this.methodBase.prototype.Params = null;
    this.methodBase.prototype.Callback = null;
    this.methodBase.prototype.__typeName = 'methodBase';
    this.methodBase.prototype.__class = true;
};
