plainblack/Lacuna-Web-Client

View on GitHub
app/js-yui/rpc.js

Summary

Maintainability
C
1 day
Test Coverage
/**
* Code taken from inputEx's  (http://javascript.neyric.com/inputex/) rpc library and slightly modified
*/

var server              = require('js/server');

YAHOO.namespace("rpc");

if (typeof YAHOO.rpc.Service == "undefined" || !YAHOO.rpc.Service) {

(function() {

    var Lang = YAHOO.lang,
        Util = YAHOO.util;

    var Service = function(smd, callback, baseUrl) {

       if( Lang.isString(smd) ) {
          this.smdUrl = smd;
          this.fetch(smd, callback);
       }
       else if( Lang.isObject(smd) ) {
          this._smd = smd;
          this._baseUrl = baseUrl;
          this.process(callback);
       }
       else {
          throw new Error("smd should be an object or an url");
       }

    };

    Service.prototype = {

        /**
        * Generate the function from a service definition
        * @method _generateService
        * @param {String} serviceName
        * @param {Method definition} method
        */
        _generateService: function(serviceName, method) {

            if(this[method]){
                throw new Error("WARNING: " + serviceName + " already exists for service. Unable to generate function");
            }
            method.name = serviceName;

            var self = this;
            var func = function(oParams, opts) {
                // Note: oParams = Object Parameters.
                var smd = self._smd;
                var baseUrl = self._baseUrl;

                var envelope = YAHOO.rpc.Envelope[method.envelope || smd.envelope];

                var callback = {
                    success: function(o) {
                        var results = envelope.deserialize(o);

                        // Send the status block off to relevant stores to update UI.
                        if (results.result && results.result.status) {
                            YAHOO.lacuna.Game.ProcessStatus(results.result.status);
                            server.splitStatus(results.result.status);
                        }

                        opts.success.call(opts.scope || self, results);
                    },
                    failure: function(o) {
                        if (Lang.isFunction(opts.failure) ) {
                            var results;
                            try {
                                results = envelope.deserialize(o);
                            }
                            catch(e) {
                                results = o;
                            }
                            opts.failure.call(opts.scope || self, results);
                        }
                    },
                    scope: self
                };

                if (opts.timeout) {
                    callback.timeout = opts.timeout;
                }

                var params = [], p;

                // Handle any SMD parameters.
                if (smd.additionalParameters && Lang.isArray(smd.parameters)) {
                    for (var i = 0; i < smd.parameters.length; i++) {
                        p = smd.parameters[i];
                        params.push(p["default"]);
                    }
                }

                // Then make sure that all the other params are in order.
                for (var i = 0; i < method.parameters.length; i++) {
                    params.push(oParams[method.parameters[i].name]);
                }

                // Now make sure that it all came out right.
                if (params) {
                    if (!params[0] || params[0].name == 'args') {
                        params = oParams;
                    }
                }
                else {
                    params = oParams;
                }

//                console.log('Calling ' + method.name + ' with the parameters of ' + Lang.JSON.stringify(params) + '.'); //debug
                var url = opts.target || method.target || smd.target;
                var urlRegexp = /^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/i;

                   if(smd.target && !url.match(urlRegexp) && url != smd.target) {
                    url = smd.target+url;
                }

                if (!!this.smdUrl && !url.match(urlRegexp)) {
                    // URL is still relative !
                    var a=this.smdUrl.split('/');
                    a[a.length-1]="";
                    url = a.join("/")+url;
                }
                else if (baseUrl) {
                    baseUrl = baseUrl.replace(/\/?$/,'/');
                    url = url.replace(/^\//, baseUrl);
                }

                var r = {
                    target: url,
                    callback: callback,
                    data: params,
                    origData: oParams,
                    opts: opts,
                    callbackParamName: method.callbackParamName || smd.callbackParamName,
                    transport: method.transport || smd.transport
                };

                //YAHOO.log('Sending ' + r.data + '.', 'debug', 'RPC._generateService');//debug

                var serialized = envelope.serialize(smd, method, params);
                Lang.augmentObject(r, serialized, true);

                return YAHOO.rpc.Transport[r.transport].call(self, r);
            };

            func.name = serviceName;
            func.description = method.description;
            func._parameters = method.parameters;

            return func;

        },

        /**
        * Process the SMD definition.
        * @method process
        */
        process: function(callback) {

            var serviceDefs = this._smd.services;

            // Generate the methods to this object
            for(var serviceName in serviceDefs){
                if( serviceDefs.hasOwnProperty(serviceName) ) {

                    // Get the object that will contain the method.
                    // handles "namespaced" services by breaking apart by '.'
                    var current = this;
                    var pieces = serviceName.split(".");
                    for(var i=0; i< pieces.length-1; i++){
                        current = current[pieces[i]] || (current[pieces[i]] = {});
                    }

                    current[pieces[pieces.length-1]] =    this._generateService(serviceName, serviceDefs[serviceName]);
                }
            }

            // call the success handler
            if(Lang.isObject(callback) && Lang.isFunction(callback.success)) {
                callback.success.call(callback.scope || this);
            }

        },

        /**
        * Download the SMD at the given url
        * @method fetch
        * @param {String} Absolute or relative url
        */
        fetch: function(url, callback) {
            if(YAHOO.rpc.Service._smdCache[url]) {
                this._smd = YAHOO.rpc.Service._smdCache[url];
                this.process(callback);
            }
            else {
                // TODO: if url is not in the same domain, we should use jsonp !
                Util.Connect.asyncRequest('GET', url, {
                    success: function(o) {
                        try {
                            this._smd = Lang.JSON.parse(o.responseText);
                            YAHOO.rpc.Service._smdCache[url] = this._smd;
                            this.process(callback);
                        }
                        catch(ex) {
                            //YAHOO.log(ex);
                            if( Lang.isFunction(callback.failure) ) {
                                callback.failure.call(callback.scope || this, {error: ex});
                            }
                        }
                    },
                    failure: function(o) {
                        if( Lang.isFunction(callback.failure) ) {
                            callback.failure.call(callback.scope || this, {error: "unable to fetch url "+url});
                        }
                    },
                    scope: this
                });
            }
        }

    };

    Service._smdCache = {}; //collection of smd objects by URL
    Service._requestId = 1;


    /**
     * YAHOO.rpc.Transport
     * @class YAHOO.rpc.Transport
     * @static
     */
    YAHOO.rpc.Transport = {
        /**
        * Build a ajax request using 'POST' method
        * @method POST
        * @param {Object} r Object specifying target, callback and data attributes
        */
        "POST": function(r) {
            return Util.Connect.asyncRequest('POST', r.target, r.callback, r.data );
        },

        /**
        * Build a ajax request using 'GET' method
        * @method GET
        * @param {Object} r Object specifying target, callback and data attributes
        */
        "GET": function(r) {
            return Util.Connect.asyncRequest('GET', r.target + (r.data ? '?'+r.data : ''), r.callback, '');
        },


        jsonp_id: 0,
        /**
        * Receive data through JSONP (insert a script tag within the page)
        * @method JSONP
        * @param {Object} r Object specifying target, callback and data attributes
        */
        "JSONP": function(r) {
            r.callbackParamName = r.callbackParamName || "callback";
            var fctName = encodeURIComponent("YAHOO.rpc.Transport.JSONP.jsonpCallback"+YAHOO.rpc.Transport.jsonp_id);
            YAHOO.rpc.Transport.JSONP["jsonpCallback"+YAHOO.rpc.Transport.jsonp_id] = function(results) {
                if(Lang.isObject(r.callback) && Lang.isFunction(r.callback.success)) {
                    r.callback.success.call(r.callback.scope || this, results);
                }
            };
            YAHOO.rpc.Transport.jsonp_id+=1;
            return Util.Get.script( r.target + ((r.target.indexOf("?") == -1) ? '?' : '&') + r.data + "&"+r.callbackParamName+"="+fctName);
        }
    };


    /**
     * YAHOO.rpc.Envelope
     * @class YAHOO.rpc.Envelope
     * @static
     */
    YAHOO.rpc.Envelope = {
        /**
        * JSON-RPC-2.0 envelope
        * @class YAHOO.rpc.Envelope.JSON-RPC-2.0
        * @static
        */
        "JSON-RPC-2.0": {
            /**
            * serialize
            */
            serialize: function(smd, method, data) {
                return {
                    data: Lang.JSON.stringify({
                        "id": YAHOO.rpc.Service._requestId++,
                        "method": method.name,
                        "jsonrpc": "2.0",
                        "params": data
                    })
                };
            },
            /**
            * deserialize
            */
            deserialize: function(results) {
                if(results.getResponseHeader && (results.getResponseHeader["Content-Type"] == "application/json-rpc" || results.getResponseHeader["Content-Type"] == "application/json")) {
                    return Lang.JSON.parse(results.responseText);
                }
                else {
                    if(results.status == -1) {
                        return {"error":{"message":"The Request has been Aborted because it was taking too long."}};
                    }
                    else if(results.status === 0) {
                        return {"error":{"message":"Communication with the server has been interrupted for an unknown reason."}};
                    }
                    else {
                        // YUI loses headers for Cross-Origin requests, so try as JSON anyway
                        try {
                            return Lang.JSON.parse(results.responseText);
                        }
                        catch(e) {
                            return {"error":{"message":"Response Content-Type is not JSON"}};
                        }
                    }
                }
            }
        }

    };

    //set connects post header to json
    Util.Connect.setDefaultPostHeader("application/json");
    Util.Connect.setDefaultPostHeader(true);
    //assign to global
    YAHOO.rpc.Service = Service;

})();
YAHOO.register("rpc", YAHOO.rpc.Service, {version: "1", build: "0"});

}