jQuery.jsonrpc = function(options) {
    /* Create an object that can be used to make JSON RPC calls. */
    return new (function(options){
        var self = this;
        this.options = jQuery.extend({
            url: '',
            version: '',
            async: false,
            error: function (r, s, e) {
        		throw 'unable to complete rpc request';
        	},
        	success: null,
        	start: null
        },options);
        this.sequence = 1;
        this.error = false;
        this.strstr = function(haystack, needle, bool) {
            var pos = 0;
            haystack += '';
            pos = haystack.indexOf( needle );
            if (pos == -1) {
                return false;
            } else {
                if (bool) {
                    return haystack.substr( 0, pos );
                } else {
                    return haystack.slice( pos );
                }
            }
        };
        this.isset = function() {
            var a=arguments, l=a.length, i=0;
            if (l===0) {
                throw 'Empty isset'; 
            }
            while (i!==l) {
            	if (typeof(a[i])=='undefined' || a[i]===null) { 
                    return false; 
                } else { 
                    i++; 
                }
            }
            return true;
        };
        this.call_array = function x(key, params) {
            var id = (self.sequence++);
            var reply = [];
            if(self.options.version == 1)
                var tosend = {method: key,params: params,id: id};
            else
                var tosend = {jsonrpc: '2.0',method: key,params: params,id: id};
            jQuery.ajax({
                async: self.options.async,
                contentType: 'application/json',
                type: self.options.transport,
                processData: false,
                dataType: 'json',
                url: self.options.url,
                cache: false,
                data: JSON.stringify(tosend),
                beforeSend: function() {
            		if (typeof(self.options.start) == 'function') {
            			self.options.start();
            		}
            	},
                error: function(req,stat,err){
            		if (typeof(self.options.error) == 'function') {
            			self.options.error(req, stat, err);
            		}
            	},
                success: function(inp) {
                	if (inp.id != id)
                		throw 'Rpc Security Error: The response id does not match with the request id. (Request: '+id+', Response: '+inp.id+')';
                	if (self.isset(inp.error))
                		throw 'Rpc Error '+inp.error.code+': '+inp.error.message;
                	reply = inp.result;
            		if (typeof(self.options.success) == 'function') {
            			self.options.success(reply);
            		}
                }
            });
            return reply;
        };
        this.call = function() {
            var params = new Array();
            if (arguments.length == 0) {
            	throw 'First parameter is required.';
            }
            for(var i = 1; i < arguments.length; i++){
                params.push(arguments[i]);
            }
            var key = arguments[0];
            try {
            	var result = this.call_array(key, params);
            	return result;
            } catch (e) {
            	throw e;
            }
        };
        jQuery.ajax({
            async: self.options.async,
            contentType: 'application/json',
            type: 'get',
            processData: false,
            dataType: 'json',
            url: self.options.url,
            cache: false,
            error: function(req,stat,err){
                self.error = true;
                self.error_message = stat;
                self.error_request = req;
            },
            success: function(data){
            	if (!self.isset(data.methods)) {
            		self.options.transport='post';
            		return;
            	}
                if(!self.options.version){
                    if(data.envelope == "JSON-RPC-1.0"){
                        self.options.version = 1;
                    }else{
                        self.options.version = 2;
                    }
                }
                self.options.transport = data.transport;
                jQuery.each(data.methods,function(key,val){
                	var p = self;
                	var m = key;
                	if  (self.strstr(key, '.')) {
                		var k = key.split('.');
                		for (var i = 0; i < k.length -1; i++) {
                			if (!self.isset(p[k[i]]))
                				p[k[i]] = new Object();
                			p = p[k[i]];
                		}
                		m=k[k.length-1];
                	}
                    p[m] = function(){
                        var params = new Array();
                        for(var i = 0; i < arguments.length; i++){
                            params.push(arguments[i]);
                        }
                        try {
                        	var result = self.call_array(key, params);
                        	return result;
                        } catch (e) {
                        	throw e;
                        }
                    };
                });
            }
        });
        return this;
    })(options);
};
