/** * Prado AJAX service. The default service provider is JPSpan. */ Prado.AJAX = { Service : 'Prototype' }; /** * Parse and execute javascript embedded in html. */ Prado.AJAX.EvalScript = function(output) { var match = new RegExp(Ajax.Updater.ScriptFragment, 'img'); var scripts = output.match(match); if (scripts) { match = new RegExp(Ajax.Updater.ScriptFragment, 'im'); setTimeout((function() { for (var i = 0; i < scripts.length; i++) eval(scripts[i].match(match)[1]); }).bind(this), 50); } } /** * AJAX service request using Prototype's AJAX request class. */ Prado.AJAX.Request = Class.create(); Prado.AJAX.Request.prototype = Object.extend(Ajax.Request.prototype, { /** * Evaluate the respond JSON data, override parent implementing. * If default eval fails, try parsing the JSON data (slower). */ evalJSON: function() { try { var json = this.transport.getResponseHeader('X-JSON'), object; object = eval(json); return object; } catch (e) { if(isString(json)) { return Prado.AJAX.JSON.parse(json); } } }, respondToReadyState: function(readyState) { var event = Ajax.Request.Events[readyState]; var transport = this.transport, json = this.evalJSON(); if(event == 'Complete' && transport.status) Ajax.Responders.dispatch('on' + transport.status, this, transport, json); if (event == 'Complete') (this.options['on' + this.transport.status] || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] || Prototype.emptyFunction)(transport, json); (this.options['on' + event] || Prototype.emptyFunction)(transport, json); Ajax.Responders.dispatch('on' + event, this, transport, json); /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ if (event == 'Complete') this.transport.onreadystatechange = Prototype.emptyFunction; } }); Prado.AJAX.Error = function(e, code) { e.name = 'Prado.AJAX.Error'; e.code = code; return e; } /** * Post data builder, serialize the data using JSON. */ Prado.AJAX.RequestBuilder = Class.create(); Prado.AJAX.RequestBuilder.prototype = { initialize : function() { this.body = ''; this.data = []; }, encode : function(data) { return Prado.AJAX.JSON.stringify(data); }, build : function(data) { var sep = ''; for ( var argName in data) { if(isFunction(data[argName])) continue; try { this.body += sep + argName + '='; this.body += encodeURIComponent(this.encode(data[argName])); } catch (e) { throw Prado.AJAX.Error(e, 1006); } sep = '&'; } }, getAll : function() { this.build(this.data); return this.body; } } Prado.AJAX.RemoteObject = function(){}; /** * AJAX service request for Prado RemoteObjects */ Prado.AJAX.RemoteObject.Request = Class.create(); Prado.AJAX.RemoteObject.Request.prototype = Object.extend(Prado.AJAX.Request.prototype, { /** * Initialize the RemoteObject Request, overrides parent * implementation by delaying the request to invokeRemoteObject. */ initialize : function(options) { this.transport = Ajax.getTransport(); this.setOptions(options); this.post = new Prado.AJAX.RequestBuilder(); }, /** * Call the remote object, * @param string the remote server url * @param array additional arguments */ invokeRemoteObject : function(url, args) { this.initParameters(args); this.options.postBody = this.post.getAll(); this.request(url); }, /** * Set the additional arguments as post data with key '__parameters' */ initParameters : function(args) { this.post.data['__parameters'] = []; for(var i = 0; i<args.length; i++) this.post.data['__parameters'][i] = args[i]; } }); /** * Base proxy class for Prado RemoteObjects via AJAX. * e.g. * <code> * var TestObject1 = Class.create(); * TestObject1.prototype = Object.extend(new Prado.AJAX.RemoteObject(), * { * initialize : function(handlers, options) * { * this.__serverurl = 'http://127.0.0.1/.....'; * this.baseInitialize(handlers, options); * } * * method1 : function() * { * return this.__call(this.__serverurl, 'method1', arguments); * } * }); *</code> * And client usage, * <code> * var test1 = new TestObject1(); //create new remote object * test1.method1(); //call the method, no onComplete hook * * var onComplete = { method1 : function(result){ alert(result) } }; * //create new remote object with onComplete callback * var test2 = new TestObject1(onComplete); * test2.method1(); //call it, on success, onComplete's method1 is called. * </code> */ Prado.AJAX.RemoteObject.prototype = { baseInitialize : function(handlers, options) { this.__handlers = handlers || {}; this.__service = new Prado.AJAX.RemoteObject.Request(options); }, __call : function(url, method, args) { this.__service.options.onSuccess = this.__onSuccess.bind(this); this.__callback = method; return this.__service.invokeRemoteObject(url+"/"+method, args); }, __onSuccess : function(transport, json) { if(this.__handlers[this.__callback]) this.__handlers[this.__callback](json, transport.responseText); } }; /** * Respond to Prado AJAX request exceptions. */ Prado.AJAX.Exception = { /** * Server returns 505 exception. Just log it. */ "on505" : function(request, transport, e) { var msg = 'HTTP '+transport.status+" with response"; Logger.error(msg, transport.responseText); Logger.exception(e); }, onComplete : function(request, transport, e) { if(transport.status != 505) { var msg = 'HTTP '+transport.status+" with response : \n"; msg += transport.responseText + "\n"; msg += "Data : \n"+e; Logger.warn(msg); } }, format : function(e) { var msg = e.type + " with message \""+e.message+"\""; msg += " in "+e.file+"("+e.line+")\n"; msg += "Stack trace:\n"; var trace = e.trace; for(var i = 0; i<trace.length; i++) { msg += " #"+i+" "+trace[i].file; msg += "("+trace[i].line+"): "; msg += trace[i]["class"]+"->"+trace[i]["function"]+"()"+"\n"; } return msg; }, logException : function(e) { var msg = Prado.AJAX.Exception.format(e); Logger.error("Server Error "+e.code, msg); } } //Add HTTP exception respones when logger is enabled. Event.OnLoad(function() { if(typeof Logger != "undefined") { Logger.exception = Prado.AJAX.Exception.logException; Ajax.Responders.register(Prado.AJAX.Exception); } }); /** * Prado Callback service that provides component intergration, * viewstate (read only), and automatic form data serialization. * Usage: <code>new Prado.AJAX.Callback('MyPage.MyComponentID.raiseCallbackEvent', options)</code> * These classes should be called by the components developers. * For inline callback service, use <t>Prado.Callback(callbackID, params)</t>. */ Prado.AJAX.Callback = Class.create(); Prado.AJAX.Callback.prototype = Object.extend(new Prado.AJAX.RemoteObject(), { /** * Create and request a new Prado callback service. * @param string the callback ID, must be of the form, <t>ClassName.ComponentID.MethodName</t> * @param list options with list key onCallbackReturn, and more. * */ initialize : function(ID, options) { if(!isString(ID)) throw new Error('A Control ID must be specified'); this.baseInitialize(this, options); this.options = options || []; this.__service.post.data['__ID'] = ID; this.requestCallback(); }, /** * Get form data for components that implements IPostBackHandler. */ collectPostData : function() { var IDs = Prado.AJAX.Callback.IDs; this.__service.post.data['__data'] = {}; for(var i = 0; i<IDs.length; i++) this.__service.post.data['__data'][IDs[i]] = $F(IDs[i]); }, /** * Prepares and calls the AJAX request. * Collects the data from components that implements IPostBackHandler * and the viewstate as part of the request payload. */ requestCallback : function() { this.collectPostData(); return this.__call(Prado.AJAX.Callback.Server, 'handleCallback', this.options.params); }, /** * On callback request return, call the onSuccess function. */ handleCallback : function(result, output) { this.options.onSuccess(result, output); } }); //Available callback service Prado.AJAX.Callback.Server = ''; //List of IDs that implements IPostBackHandler Prado.AJAX.Callback.IDs = []; /** * Simple AJAX callback interface, suitable for inline javascript. * e.g., <code><a href="..." onclick="Prado.Callback('..', 'Hello');">Click me</a></code> * @param string callback ID * @param array parameters to pass to the callback service */ Prado.Callback = function(ID, params, onSuccess) { var options = { 'params' : [params] || [], 'onSuccess' : onSuccess || Prototype.emptyFunction }; new Prado.AJAX.Callback(ID, options); return false; }