/* ========= joms.ajax ========= joms.ajax = jax + jQuery.ajax(). Jax is like the protocol. jQuery.ajax() is like the transport. Process flow: Jax wraps data. | jQuery.ajax() sends the data to server. | Server returns jax responses. | jQuery.ajax() delegate jax responses. | jax.processResponse() executes jax response. | jQuery.ajax() extract callback data embedded within jax responses. | jQuery.ajax() pass the callback data to error, success & complete handler. This is done without modifying neither jax or jQuery.ajax() library. So existing jax.call() can execute side-by-side without problems. ============================ How to use joms.ajax.call()? ============================ joms.ajax.call(sFunc, sArgs, callback) joms.ajax.call('controller,function', [arg1, arg2], function(data) { // What to do when the ajax call is successful }); joms.ajax.call(sFunc, sArgs, settings) joms.ajax.call('controller,function', [arg1, arg2] { success: function(data) { // What to do when the ajax call is successful }, error: function(data) { // What to do when the ajax call is not successful // e.g. timeout, 404, 500, etc. }, // For more settings to fiddle with, look up jQuery.ajax() docs. }); =========================================== Advantage of joms.ajax() with example usage =========================================== 1. Ability to use PHP associative array as JSON as callback data, e.g. // ---- PHP side --- // $callbackData['url'] = 'http://www.jomsocial.com/' $callbackData['caption'] = 'JomSocial' $objResponse->addScriptCall('__callback', $callbackData); // ---- JS side --- // joms.ajax.call('controller,function', [arg1, arg2], function(data) { console.log(data.url); // output http://www.jomsocial.com console.log(data.caption); // output JomSocial }); 2. Ability to handle server timeouts or other errors, e.g. joms.ajax.call('apps,ajaxSetOrder', [appOrder], { success: function(data) { // Remove saving indicator jQuery('#app-position').removeClass('onSave'); }, error: function(data) { // Revert app ordering jQuery('#app-position').sortable('cancel'); } }); 3. Continue writing relevant codes within the same function in JS, instead of writing them as addScriptCall() in PHP, while preserving the function/variable scope those relevant codes could access and reuse, e.g. function removePhoto() { var photo = jQuery('#photo-63'); var photoId = photo.attr('id').split('-')[1]; joms.ajax.call('photos,ajaxRemovePhoto', [photoId], function(data) { // Now I can use back the 'photo' variable // $objResponse->addScriptCall('jQuery("#photo-' + $photoId + ')').remove'); jQuery(photo).remove(); }); } */ joms.extend({ ajax: { execute: function(o) { // Rearrange jQuery.ajax callback order. // jQuery.ajax : beforeSend -> dataFilter -> success/error -> complete // joms.ajax : beforeSend -> beforeDataFilter -> dataFilter -> afterDataFilter -> fail/success/error -> complete // // joms.ajax callback handlers // - beforeSend: function(xhr) // - fail : function(xhr, status, e) // - beforeDataFilter: function(jaxData) // - afterDataFilter : function(jomsData) // - success : function(arg1, arg2...) // from $objResponse->addScriptCall('__callback', $arg1, $arg2); // - error : function(arg1, arg2...) // from $objResponse->addScriptCall('__throwError', $arg1, $arg2); // - complete : function(xhr, status) var settings = {}; joms.jQuery.extend(settings, o); settings.error = function() { if (o.fail) o.fail.apply(this, arguments); } settings.dataFilter = function(jaxData, type) { if (o.beforeDataFilter) o.beforeDataFilter(jaxData); // Delegate jax commands to jax.processReponse() jax.processResponse(jaxData); // Extract callback data embedded inside jaxData var jomsData = { success: joms.ajax.extractJomsData(jaxData, '__callback'), error : joms.ajax.extractJomsData(jaxData, '__throwError') } if (o.afterDataFilter) o.afterDataFilter(jomsData); return jomsData; } settings.success = function(jomsData, status) { if (jomsData.error.length>0 && o.error) { joms.jQuery.each(jomsData.error, function(i, args) { o.error.apply(this, args); }); return; } if (jomsData.success.length>0 && o.success) { joms.jQuery.each(jomsData.success, function(i, args) { o.success.apply(this, args); }); } } // Create a dummy __callback function so we don't have // waste computing cycles trying to filter them out in // filterJaxResponse(); window.__callback = window.__throwError = function() {}; // Use jQuery.ajax() (replacing the not-so-fun jax.submitTask()) joms.jQuery.ajax(settings); }, /* * joms.ajax.call() * * @param sFunc {String} The server function to call in the format of "controller,function" * @param sArgs {Array} The arguments to pass to the server function * @param o {Function/Object} If passed as a function, it is treated as callback function * that gets executed by 'success' event. * If passed as an object, it is treated as ajax settings. */ call: function(sFunc, sArgs, o) { var settings = { url: jax_live_site, type: 'POST', data: { option : 'community', task : 'azrul_ajax', no_html: 1, func : sFunc }, dataType: 'text' }; // To fix CSRF issues. settings.data[ jax_token_var ] = 1; // Build Jax-style arguments for the url param joms.jQuery.extend(settings.data, this.buildSArgs(sArgs)); // If o is callback function, wrap it back as an ajax settings object. if (typeof o=='function') o = {success: o}; // Override default settings with provided settings joms.jQuery.extend(settings, o); this.execute(settings); }, /* * joms.ajax.buildSArgs() * [Internal function] * * Modified from jax.call(). * Instead of building arguments into a string, * this function builds arguments into a key/value object, * and jQuery.ajax() function will serialize them. * * @param sArgs {array} An array of arguments to pass into the server function. */ buildSArgs: function(sArgs) { var data = {}; joms.jQuery(sArgs).each(function(i, a) { if (typeof a=='string') a.replace(/"/g, '"'); if (!jax.isArray(a)) a = new Array('_d_', encodeURIComponent(a)); data['arg'+i] = jax.stringify(a); }) return data; }, extractJomsData: function(jaxData, handler) { var jomsData = []; joms.jQuery.each(eval(jaxData), function() { if (this[0]=='cs' && this[1]==handler) { jomsData.push(this.slice(3)[0]); } }); return jomsData; } } });