From 24cc54caf662e1be955e18c9f52e95ccb65cda4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Wed, 2 Mar 2016 15:12:34 +0100 Subject: [PATCH 01/11] New Ajax API: It's backward compatible. New features: it supports JSON responses with multiple commands. It also supports different kind of commands such as insert, settings, invoke, remove, etc... --- e107_web/js/core/all.jquery.js | 475 ++++++++++++++++++++------------- 1 file changed, 294 insertions(+), 181 deletions(-) diff --git a/e107_web/js/core/all.jquery.js b/e107_web/js/core/all.jquery.js index 434101af6..8dcd3522e 100644 --- a/e107_web/js/core/all.jquery.js +++ b/e107_web/js/core/all.jquery.js @@ -6,6 +6,8 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; (function ($) { + e107.ajax = e107.ajax || {}; + e107.callbacks = e107.callbacks || {}; /** @@ -117,208 +119,319 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; }); /** - * Behavior to attach a click event to links with .e-ajax class. + * Get a reasonable default event handler for a (jQuery) element. * - * @type {{attach: Function}} + * @param $element + * JQuery element. */ - e107.behaviors.eAjaxLink = { - attach: function (context, settings) + e107.ajax.getDefaultEventHandler = function ($element) + { + var event = 'click'; // Default event handler. + var tag = $element.prop("tagName").toLowerCase(); + + if(tag == 'input') { - $(context).find('a.e-ajax').once('e-ajax-link').each(function () + var type = $element.attr('type').toLowerCase(); + + switch(type) { - $(this).click(function () - { - // Old way - href='myscript.php#id-to-target - var href = $(this).attr("href"); - // Target container for result. - var target = $(this).attr("data-target"); - // Image to show loading. - var loading = $(this).attr('data-loading'); - // If this is a navigation controller, e.g. pager... - var nav = $(this).attr('data-nav-inc'); - // Method: 'replaceWith', 'append', 'prepend', 'before', 'after', 'html' (default). - var method = $(this).attr('data-method'); + case 'submit': + case 'button': + // Pressing the ENTER key within a textfield triggers the click event of + // the form's first submit button. Triggering Ajax in this situation + // leads to problems, like breaking autocomplete textfields, so we bind + // to mousedown instead of click. + event = 'mousedown'; + break; - if(nav != null) - { - // Modify data-src value for next/prev. 'from=' - e107.callbacks.eNav(this, '.e-ajax'); - } + case 'radio': + case 'checkbox': + event = 'change'; + break; - // URL for Ajax request. - var handler = $(this).attr("data-src"); - - var $target = $("#" + target); - var html = null; // Ajax result. - var $loadingImage = null; - - // TODO: set default loading icon? - if(loading != null) - { - $loadingImage = $(""); - $(this).after($loadingImage); - } - - if(target == null || handler == null) // Old way - href='myscript.php#id-to-target - { - if(href != null) - { - var tmp = href.split('#'); - var id = tmp[1]; - - if(handler == null) - { - handler = tmp[0]; - } - - if(target == null) - { - $target = $('#' + id); - } - } - } - - $.ajax({ - type: 'GET', - url: handler, - complete: function () - { - if($loadingImage) - { - $loadingImage.remove(); - } - }, - success: function (data) - { - switch(method) - { - case 'replaceWith': - html = $.parseHTML(data); - $target.replaceWith(html); - break; - - case 'append': - html = $.parseHTML(data); - $target.append(html); - break; - - case 'prepend': - html = $.parseHTML(data); - $target.prepend(html); - break; - - case 'before': - html = $.parseHTML(data); - $target.before(html); - break; - - case 'after': - html = $.parseHTML(data); - $target.after(html); - break; - - case 'html': - default: - $target.html(data).hide().show("slow"); - break; - } - - // Attach all registered behaviors to the new content. - e107.attachBehaviors(); - } - }); - - return false; - }); - }); + // text, number, password, date, datetime, datetime-local, month, week, time, + // email, search, tel, url, color, range + default: + event = 'blur'; + break; + } } + else + { + switch(tag) + { + case 'button': + // Pressing the ENTER key within a textfield triggers the click event of + // the form's first submit button. Triggering Ajax in this situation + // leads to problems, like breaking autocomplete textfields, so we bind + // to mousedown instead of click. + event = 'mousedown'; + break; + + case 'select': + event = 'change'; + break; + + case 'textarea': + event = 'blur'; + break; + } + } + + return event; }; /** - * Behavior to attach a change event to selects with .e-ajax class. + * Handler fo Ajax requests. * - * @type {{attach: Function}} + * @param $element + * JQuery element which fired the event. + * @param options + * An object with Ajax request options. */ - e107.behaviors.eAjaxSelect = { + e107.ajax.ajaxRequestHandler = function ($element, options) + { + var $loadingImage = null; + + // Loading image. + if(options.loading != null) + { + $loadingImage = $(options.loading); + $element.after($loadingImage); + } + + // Old way - href='myscript.php#id-to-target. + if(options.target == null || options.url == null) + { + if(options.href != null) + { + var tmp = options.href.split('#'); + var id = tmp[1]; + + if(options.url == null) + { + options.url = tmp[0]; + } + + if(options.target == null) + { + options.target = id; + } + } + } + + var form = $element.closest("form").attr('id'); + var data = $('#' + form).serialize(); + + $.ajax({ + type: options.type || 'POST', + url: options.url, + data: data || '', + complete: function () + { + if($loadingImage) + { + $loadingImage.remove(); + } + }, + success: function (response) + { + var $target = $("#" + options.target); + var jsonObject = null; + + if(typeof response == 'string') + { + try + { + jsonObject = $.parseJSON(response); + } catch(e) + { + // Not JSON. + } + } + + if(typeof jsonObject == 'object') + { + // If result is JSON. + e107.ajax.ajaxJsonResponseHandler($target, options, jsonObject); + } + else + { + // If result is a simple text/html. + e107.ajax.ajaxResponseHandler($target, options, response); + } + } + }); + }; + + /** + * Handler for JSON responses. Provides a series of commands that the server + * can request the client perform. + * + * @param $target + * JQuery (target) object. + * @param options + * Object with options for Ajax request. + * @param commands + * JSON object with commands. + */ + e107.ajax.ajaxJsonResponseHandler = function ($target, options, commands) + { + $.each(commands, function (command) + { + switch(command.command) + { + // Command to insert new content into the DOM. + case 'insert': + // Get target selector from the response. If it is not there, default to our presets. + $target = command.selector ? $(command.selector) : $target; + e107.ajax.ajaxResponseHandler($target, options, command.data); + break; + + // Command to remove a chunk from the page. + case 'remove': + e107.detachBehaviors($(command.selector)); + $(command.selector).remove(); + break; + + // Command to provide an alert. + case 'alert': + alert(command.text, command.title); + break; + + // Command to provide the jQuery css() function. + case 'css': + $(command.selector).css(command.arguments); + break; + + // Command to set the settings that will be used for other commands in this response. + case 'settings': + if(typeof command.settings == 'object') + { + $.extend(true, e107.settings, command.settings); + } + break; + + // Command to attach data using jQuery's data API. + case 'data': + $(command.selector).data(command.name, command.value); + break; + + // Command to apply a jQuery method. + case 'invoke': + var $element = $(command.selector); + $element[command.method].apply($element, command.arguments); + break; + } + }); + }; + + /** + * Handler for text/html responses. Inserting new content into the DOM. + * + * @param $target + * JQuery (target) object. + * @param options + * An object with Ajax request options. + * @param data + * Text/HTML content. + */ + e107.ajax.ajaxResponseHandler = function ($target, options, data) + { + var html = null; + + // If removing content from the wrapper, detach behaviors first. + switch(options.method) + { + case 'html': + case 'replaceWith': + e107.detachBehaviors($target); + break; + } + + // Inserting content. + switch(options.method) + { + case 'replaceWith': + html = $.parseHTML(data); + $target.replaceWith(html); + break; + + case 'append': + html = $.parseHTML(data); + $target.append(html); + break; + + case 'prepend': + html = $.parseHTML(data); + $target.prepend(html); + break; + + case 'before': + html = $.parseHTML(data); + $target.before(html); + break; + + case 'after': + html = $.parseHTML(data); + $target.after(html); + break; + + case 'html': + default: + $target.html(data).hide().show("slow"); + break; + } + + // Attach all registered behaviors to the new content. + e107.attachBehaviors(); + }; + + /** + * Attaches the AJAX behavior to each AJAX form/page elements. E107 uses + * this behavior to enhance form/page elements with .e-ajax class. + */ + e107.behaviors.eAJAX = { attach: function (context, settings) { - $(context).find('select.e-ajax').once('e-ajax-select').each(function () + $(context).find('.e-ajax').once('e-ajax').each(function () { - $(this).on('change', function () + var $this = $(this); + var event = $this.attr('data-event') || e107.ajax.getDefaultEventHandler($this); + + $this.on(event, function () { - var form = $(this).closest("form").attr('id'); + var $element = $(this); - // Target container for result. - var target = $(this).attr("data-target"); - // Image to show loading. - var loading = $(this).attr('data-loading'); - // URL for Ajax request. - var handler = $(this).attr('data-src'); - // Method: 'replaceWith', 'append', 'prepend', 'before', 'after', 'html' (default). - var method = $(this).attr('data-method'); + var ajaxOptions = { + // URL for Ajax request. + url: $element.attr('data-src'), + // Ajax type: POST or GET. + type: $element.attr('data-ajax-type'), + // Target container for result. + target: $element.attr("data-target"), + // Method: 'replaceWith', 'append', 'prepend', 'before', 'after', 'html' (default). + method: $element.attr('data-method'), + // Image to show loading. + loading: $element.attr('data-loading'), + // If this is a navigation controller, e.g. pager. + nav: $element.attr('data-nav-inc'), + // Old way - href='myscript.php#id-to-target. + href: $element.attr("href") + }; - var data = $('#' + form).serialize(); - var $target = $("#" + target); - var html = null; - var $loadingImage = null; - - // TODO: set default loading icon? - if(loading != null) + // If this is a navigation controller, e.g. pager. + if(ajaxOptions.nav != null) { - $loadingImage = $(""); - $(this).after($loadingImage); + // Modify data-src value for next/prev. 'from=' + e107.callbacks.eNav(this, '.e-ajax'); + // Update URL for Ajax request. + ajaxOptions.url = $element.attr('data-src'); + // Set Ajax type to "GET". + ajaxOptions.type = 'GET'; } - $.ajax({ - type: 'post', - url: handler, - data: data, - complete: function () - { - if($loadingImage) - { - $loadingImage.remove(); - } - }, - success: function (data) - { - switch(method) - { - case 'replaceWith': - html = $.parseHTML(data); - $target.replaceWith(html); - break; - - case 'append': - html = $.parseHTML(data); - $target.append(html); - break; - - case 'prepend': - html = $.parseHTML(data); - $target.prepend(html); - break; - - case 'before': - html = $.parseHTML(data); - $target.before(html); - break; - - case 'after': - html = $.parseHTML(data); - $target.after(html); - break; - - case 'html': - default: - $target.html(data).hide().show("slow"); - break; - } - - // Attach all registered behaviors to the new content. - e107.attachBehaviors(); - } - }); + e107.ajax.ajaxRequestHandler($element, ajaxOptions); return false; }); From 4cfa54e07cda7ab951c51e7b0c55127fe529c42c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Wed, 2 Mar 2016 15:13:42 +0100 Subject: [PATCH 02/11] Change comment for command "settings". --- e107_web/js/core/all.jquery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e107_web/js/core/all.jquery.js b/e107_web/js/core/all.jquery.js index 8dcd3522e..17ea02d1f 100644 --- a/e107_web/js/core/all.jquery.js +++ b/e107_web/js/core/all.jquery.js @@ -304,7 +304,7 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; $(command.selector).css(command.arguments); break; - // Command to set the settings that will be used for other commands in this response. + // Command to merge settings e107.settings object. case 'settings': if(typeof command.settings == 'object') { From 82bc735daa9d4447e980cf3890e711d408458943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Wed, 2 Mar 2016 15:38:44 +0100 Subject: [PATCH 03/11] Fixed JSON response handler. --- e107_web/js/core/all.jquery.js | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/e107_web/js/core/all.jquery.js b/e107_web/js/core/all.jquery.js index 17ea02d1f..85a931094 100644 --- a/e107_web/js/core/all.jquery.js +++ b/e107_web/js/core/all.jquery.js @@ -220,6 +220,12 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; } } + // BC. + if(options.target.charAt(0) != "#" && options.target.charAt(0) != ".") + { + options.target = "#" + options.target; + } + var form = $element.closest("form").attr('id'); var data = $('#' + form).serialize(); @@ -236,8 +242,8 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; }, success: function (response) { - var $target = $("#" + options.target); - var jsonObject = null; + var $target = $(options.target); + var jsonObject = response; if(typeof response == 'string') { @@ -277,8 +283,10 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; */ e107.ajax.ajaxJsonResponseHandler = function ($target, options, commands) { - $.each(commands, function (command) + $(commands).each(function () { + var command = this; + switch(command.command) { // Command to insert new content into the DOM. @@ -304,7 +312,7 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; $(command.selector).css(command.arguments); break; - // Command to merge settings e107.settings object. + // Command to set the settings that will be used for other commands in this response. case 'settings': if(typeof command.settings == 'object') { @@ -322,6 +330,20 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; var $element = $(command.selector); $element[command.method].apply($element, command.arguments); break; + + // Command to set attribute for element. + case 'attr': + // Get target selector from the response. If it is not there, default to our presets. + $target = command.selector ? $(command.selector) : $target; + $target.attr(command.name, command.value); + break; + + // Command to remove attribute from element. + case 'removeAttr': + // Get target selector from the response. If it is not there, default to our presets. + $target = command.selector ? $(command.selector) : $target; + $target.removeAttr(command.name); + break; } }); }; From d8c2dc883ddd558cf7416c1e65fbe38e9e83f584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Wed, 2 Mar 2016 16:02:53 +0100 Subject: [PATCH 04/11] Override default method by command "insert". --- e107_web/js/core/all.jquery.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/e107_web/js/core/all.jquery.js b/e107_web/js/core/all.jquery.js index 85a931094..799f123b9 100644 --- a/e107_web/js/core/all.jquery.js +++ b/e107_web/js/core/all.jquery.js @@ -293,7 +293,9 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; case 'insert': // Get target selector from the response. If it is not there, default to our presets. $target = command.selector ? $(command.selector) : $target; - e107.ajax.ajaxResponseHandler($target, options, command.data); + var newOptions = options; + newOptions.method = command.method; + e107.ajax.ajaxResponseHandler($target, newOptions, command.data); break; // Command to remove a chunk from the page. From de95262459adcf27074d5fd50e996c0f279bf79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Wed, 2 Mar 2016 16:42:48 +0100 Subject: [PATCH 05/11] Use e107.callbacks instead of creating new object. --- e107_web/js/core/all.jquery.js | 590 ++++++++++++++++----------------- 1 file changed, 295 insertions(+), 295 deletions(-) diff --git a/e107_web/js/core/all.jquery.js b/e107_web/js/core/all.jquery.js index 799f123b9..7256c5f20 100644 --- a/e107_web/js/core/all.jquery.js +++ b/e107_web/js/core/all.jquery.js @@ -118,299 +118,6 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; e107.attachBehaviors(document, e107.settings); }); - /** - * Get a reasonable default event handler for a (jQuery) element. - * - * @param $element - * JQuery element. - */ - e107.ajax.getDefaultEventHandler = function ($element) - { - var event = 'click'; // Default event handler. - var tag = $element.prop("tagName").toLowerCase(); - - if(tag == 'input') - { - var type = $element.attr('type').toLowerCase(); - - switch(type) - { - case 'submit': - case 'button': - // Pressing the ENTER key within a textfield triggers the click event of - // the form's first submit button. Triggering Ajax in this situation - // leads to problems, like breaking autocomplete textfields, so we bind - // to mousedown instead of click. - event = 'mousedown'; - break; - - case 'radio': - case 'checkbox': - event = 'change'; - break; - - // text, number, password, date, datetime, datetime-local, month, week, time, - // email, search, tel, url, color, range - default: - event = 'blur'; - break; - } - } - else - { - switch(tag) - { - case 'button': - // Pressing the ENTER key within a textfield triggers the click event of - // the form's first submit button. Triggering Ajax in this situation - // leads to problems, like breaking autocomplete textfields, so we bind - // to mousedown instead of click. - event = 'mousedown'; - break; - - case 'select': - event = 'change'; - break; - - case 'textarea': - event = 'blur'; - break; - } - } - - return event; - }; - - /** - * Handler fo Ajax requests. - * - * @param $element - * JQuery element which fired the event. - * @param options - * An object with Ajax request options. - */ - e107.ajax.ajaxRequestHandler = function ($element, options) - { - var $loadingImage = null; - - // Loading image. - if(options.loading != null) - { - $loadingImage = $(options.loading); - $element.after($loadingImage); - } - - // Old way - href='myscript.php#id-to-target. - if(options.target == null || options.url == null) - { - if(options.href != null) - { - var tmp = options.href.split('#'); - var id = tmp[1]; - - if(options.url == null) - { - options.url = tmp[0]; - } - - if(options.target == null) - { - options.target = id; - } - } - } - - // BC. - if(options.target.charAt(0) != "#" && options.target.charAt(0) != ".") - { - options.target = "#" + options.target; - } - - var form = $element.closest("form").attr('id'); - var data = $('#' + form).serialize(); - - $.ajax({ - type: options.type || 'POST', - url: options.url, - data: data || '', - complete: function () - { - if($loadingImage) - { - $loadingImage.remove(); - } - }, - success: function (response) - { - var $target = $(options.target); - var jsonObject = response; - - if(typeof response == 'string') - { - try - { - jsonObject = $.parseJSON(response); - } catch(e) - { - // Not JSON. - } - } - - if(typeof jsonObject == 'object') - { - // If result is JSON. - e107.ajax.ajaxJsonResponseHandler($target, options, jsonObject); - } - else - { - // If result is a simple text/html. - e107.ajax.ajaxResponseHandler($target, options, response); - } - } - }); - }; - - /** - * Handler for JSON responses. Provides a series of commands that the server - * can request the client perform. - * - * @param $target - * JQuery (target) object. - * @param options - * Object with options for Ajax request. - * @param commands - * JSON object with commands. - */ - e107.ajax.ajaxJsonResponseHandler = function ($target, options, commands) - { - $(commands).each(function () - { - var command = this; - - switch(command.command) - { - // Command to insert new content into the DOM. - case 'insert': - // Get target selector from the response. If it is not there, default to our presets. - $target = command.selector ? $(command.selector) : $target; - var newOptions = options; - newOptions.method = command.method; - e107.ajax.ajaxResponseHandler($target, newOptions, command.data); - break; - - // Command to remove a chunk from the page. - case 'remove': - e107.detachBehaviors($(command.selector)); - $(command.selector).remove(); - break; - - // Command to provide an alert. - case 'alert': - alert(command.text, command.title); - break; - - // Command to provide the jQuery css() function. - case 'css': - $(command.selector).css(command.arguments); - break; - - // Command to set the settings that will be used for other commands in this response. - case 'settings': - if(typeof command.settings == 'object') - { - $.extend(true, e107.settings, command.settings); - } - break; - - // Command to attach data using jQuery's data API. - case 'data': - $(command.selector).data(command.name, command.value); - break; - - // Command to apply a jQuery method. - case 'invoke': - var $element = $(command.selector); - $element[command.method].apply($element, command.arguments); - break; - - // Command to set attribute for element. - case 'attr': - // Get target selector from the response. If it is not there, default to our presets. - $target = command.selector ? $(command.selector) : $target; - $target.attr(command.name, command.value); - break; - - // Command to remove attribute from element. - case 'removeAttr': - // Get target selector from the response. If it is not there, default to our presets. - $target = command.selector ? $(command.selector) : $target; - $target.removeAttr(command.name); - break; - } - }); - }; - - /** - * Handler for text/html responses. Inserting new content into the DOM. - * - * @param $target - * JQuery (target) object. - * @param options - * An object with Ajax request options. - * @param data - * Text/HTML content. - */ - e107.ajax.ajaxResponseHandler = function ($target, options, data) - { - var html = null; - - // If removing content from the wrapper, detach behaviors first. - switch(options.method) - { - case 'html': - case 'replaceWith': - e107.detachBehaviors($target); - break; - } - - // Inserting content. - switch(options.method) - { - case 'replaceWith': - html = $.parseHTML(data); - $target.replaceWith(html); - break; - - case 'append': - html = $.parseHTML(data); - $target.append(html); - break; - - case 'prepend': - html = $.parseHTML(data); - $target.prepend(html); - break; - - case 'before': - html = $.parseHTML(data); - $target.before(html); - break; - - case 'after': - html = $.parseHTML(data); - $target.after(html); - break; - - case 'html': - default: - $target.html(data).hide().show("slow"); - break; - } - - // Attach all registered behaviors to the new content. - e107.attachBehaviors(); - }; - /** * Attaches the AJAX behavior to each AJAX form/page elements. E107 uses * this behavior to enhance form/page elements with .e-ajax class. @@ -421,7 +128,7 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; $(context).find('.e-ajax').once('e-ajax').each(function () { var $this = $(this); - var event = $this.attr('data-event') || e107.ajax.getDefaultEventHandler($this); + var event = $this.attr('data-event') || e107.callbacks.getDefaultEventHandler($this); $this.on(event, function () { @@ -455,7 +162,7 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; ajaxOptions.type = 'GET'; } - e107.ajax.ajaxRequestHandler($element, ajaxOptions); + e107.callbacks.ajaxRequestHandler($element, ajaxOptions); return false; }); @@ -589,6 +296,299 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; } }; + /** + * Get a reasonable default event handler for a (jQuery) element. + * + * @param $element + * JQuery element. + */ + e107.callbacks.getDefaultEventHandler = function ($element) + { + var event = 'click'; // Default event handler. + var tag = $element.prop("tagName").toLowerCase(); + + if(tag == 'input') + { + var type = $element.attr('type').toLowerCase(); + + switch(type) + { + case 'submit': + case 'button': + // Pressing the ENTER key within a textfield triggers the click event of + // the form's first submit button. Triggering Ajax in this situation + // leads to problems, like breaking autocomplete textfields, so we bind + // to mousedown instead of click. + event = 'mousedown'; + break; + + case 'radio': + case 'checkbox': + event = 'change'; + break; + + // text, number, password, date, datetime, datetime-local, month, week, time, + // email, search, tel, url, color, range + default: + event = 'blur'; + break; + } + } + else + { + switch(tag) + { + case 'button': + // Pressing the ENTER key within a textfield triggers the click event of + // the form's first submit button. Triggering Ajax in this situation + // leads to problems, like breaking autocomplete textfields, so we bind + // to mousedown instead of click. + event = 'mousedown'; + break; + + case 'select': + event = 'change'; + break; + + case 'textarea': + event = 'blur'; + break; + } + } + + return event; + }; + + /** + * Handler fo Ajax requests. + * + * @param $element + * JQuery element which fired the event. + * @param options + * An object with Ajax request options. + */ + e107.callbacks.ajaxRequestHandler = function ($element, options) + { + var $loadingImage = null; + + // Loading image. + if(options.loading != null) + { + $loadingImage = $(options.loading); + $element.after($loadingImage); + } + + // Old way - href='myscript.php#id-to-target. + if(options.target == null || options.url == null) + { + if(options.href != null) + { + var tmp = options.href.split('#'); + var id = tmp[1]; + + if(options.url == null) + { + options.url = tmp[0]; + } + + if(options.target == null) + { + options.target = id; + } + } + } + + // BC. + if(options.target.charAt(0) != "#" && options.target.charAt(0) != ".") + { + options.target = "#" + options.target; + } + + var form = $element.closest("form").attr('id'); + var data = $('#' + form).serialize(); + + $.ajax({ + type: options.type || 'POST', + url: options.url, + data: data || '', + complete: function () + { + if($loadingImage) + { + $loadingImage.remove(); + } + }, + success: function (response) + { + var $target = $(options.target); + var jsonObject = response; + + if(typeof response == 'string') + { + try + { + jsonObject = $.parseJSON(response); + } catch(e) + { + // Not JSON. + } + } + + if(typeof jsonObject == 'object') + { + // If result is JSON. + e107.callbacks.ajaxJsonResponseHandler($target, options, jsonObject); + } + else + { + // If result is a simple text/html. + e107.callbacks.ajaxResponseHandler($target, options, response); + } + } + }); + }; + + /** + * Handler for JSON responses. Provides a series of commands that the server + * can request the client perform. + * + * @param $target + * JQuery (target) object. + * @param options + * Object with options for Ajax request. + * @param commands + * JSON object with commands. + */ + e107.callbacks.ajaxJsonResponseHandler = function ($target, options, commands) + { + $(commands).each(function () + { + var command = this; + + switch(command.command) + { + // Command to insert new content into the DOM. + case 'insert': + // Get target selector from the response. If it is not there, default to our presets. + $target = command.selector ? $(command.selector) : $target; + var newOptions = options; + newOptions.method = command.method; + e107.callbacks.ajaxResponseHandler($target, newOptions, command.data); + break; + + // Command to remove a chunk from the page. + case 'remove': + e107.detachBehaviors($(command.selector)); + $(command.selector).remove(); + break; + + // Command to provide an alert. + case 'alert': + alert(command.text, command.title); + break; + + // Command to provide the jQuery css() function. + case 'css': + $(command.selector).css(command.arguments); + break; + + // Command to set the settings that will be used for other commands in this response. + case 'settings': + if(typeof command.settings == 'object') + { + $.extend(true, e107.settings, command.settings); + } + break; + + // Command to attach data using jQuery's data API. + case 'data': + $(command.selector).data(command.name, command.value); + break; + + // Command to apply a jQuery method. + case 'invoke': + var $element = $(command.selector); + $element[command.method].apply($element, command.arguments); + break; + + // Command to set attribute for element. + case 'attr': + // Get target selector from the response. If it is not there, default to our presets. + $target = command.selector ? $(command.selector) : $target; + $target.attr(command.name, command.value); + break; + + // Command to remove attribute from element. + case 'removeAttr': + // Get target selector from the response. If it is not there, default to our presets. + $target = command.selector ? $(command.selector) : $target; + $target.removeAttr(command.name); + break; + } + }); + }; + + /** + * Handler for text/html responses. Inserting new content into the DOM. + * + * @param $target + * JQuery (target) object. + * @param options + * An object with Ajax request options. + * @param data + * Text/HTML content. + */ + e107.callbacks.ajaxResponseHandler = function ($target, options, data) + { + var html = null; + + // If removing content from the wrapper, detach behaviors first. + switch(options.method) + { + case 'html': + case 'replaceWith': + e107.detachBehaviors($target); + break; + } + + // Inserting content. + switch(options.method) + { + case 'replaceWith': + html = $.parseHTML(data); + $target.replaceWith(html); + break; + + case 'append': + html = $.parseHTML(data); + $target.append(html); + break; + + case 'prepend': + html = $.parseHTML(data); + $target.prepend(html); + break; + + case 'before': + html = $.parseHTML(data); + $target.before(html); + break; + + case 'after': + html = $.parseHTML(data); + $target.after(html); + break; + + case 'html': + default: + $target.html(data).hide().show("slow"); + break; + } + + // Attach all registered behaviors to the new content. + e107.attachBehaviors(); + }; + })(jQuery); $.ajaxSetup({ From 1a1a990e6b83099ac9d38792b8ad065f1b1fe4fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Wed, 2 Mar 2016 16:43:57 +0100 Subject: [PATCH 06/11] Remove unused e107.ajax object. --- e107_web/js/core/all.jquery.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/e107_web/js/core/all.jquery.js b/e107_web/js/core/all.jquery.js index 7256c5f20..930a9e828 100644 --- a/e107_web/js/core/all.jquery.js +++ b/e107_web/js/core/all.jquery.js @@ -6,8 +6,6 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; (function ($) { - e107.ajax = e107.ajax || {}; - e107.callbacks = e107.callbacks || {}; /** From 100179823776fb4a76d4ca3c31d9d55bb35076e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Wed, 2 Mar 2016 18:40:46 +0100 Subject: [PATCH 07/11] Use "target" instead of "selector". --- e107_web/js/core/all.jquery.js | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/e107_web/js/core/all.jquery.js b/e107_web/js/core/all.jquery.js index 930a9e828..70f5991f7 100644 --- a/e107_web/js/core/all.jquery.js +++ b/e107_web/js/core/all.jquery.js @@ -462,22 +462,22 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; $(commands).each(function () { var command = this; + // Get target selector from the response. If it is not there, default to our presets. + var $newtarget = command.target ? $(command.target) : $target; switch(command.command) { // Command to insert new content into the DOM. case 'insert': - // Get target selector from the response. If it is not there, default to our presets. - $target = command.selector ? $(command.selector) : $target; var newOptions = options; newOptions.method = command.method; - e107.callbacks.ajaxResponseHandler($target, newOptions, command.data); + e107.callbacks.ajaxResponseHandler($newtarget, newOptions, command.data); break; // Command to remove a chunk from the page. case 'remove': - e107.detachBehaviors($(command.selector)); - $(command.selector).remove(); + e107.detachBehaviors($(command.target)); + $(command.target).remove(); break; // Command to provide an alert. @@ -487,7 +487,7 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; // Command to provide the jQuery css() function. case 'css': - $(command.selector).css(command.arguments); + $(command.target).css(command.arguments); break; // Command to set the settings that will be used for other commands in this response. @@ -500,27 +500,23 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; // Command to attach data using jQuery's data API. case 'data': - $(command.selector).data(command.name, command.value); + $(command.target).data(command.name, command.value); break; // Command to apply a jQuery method. case 'invoke': - var $element = $(command.selector); + var $element = $(command.target); $element[command.method].apply($element, command.arguments); break; // Command to set attribute for element. case 'attr': - // Get target selector from the response. If it is not there, default to our presets. - $target = command.selector ? $(command.selector) : $target; - $target.attr(command.name, command.value); + $newtarget.attr(command.name, command.value); break; // Command to remove attribute from element. case 'removeAttr': - // Get target selector from the response. If it is not there, default to our presets. - $target = command.selector ? $(command.selector) : $target; - $target.removeAttr(command.name); + $newtarget.removeAttr(command.name); break; } }); From 4d0cdfc0ba6213b9f45855a2f2be9634f70017fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Thu, 3 Mar 2016 11:50:36 +0100 Subject: [PATCH 08/11] Add e107::ajax() to provide macro methods for createing Ajax commands. --- e107_handlers/e107_class.php | 11 ++ e107_handlers/e_ajax_class.php | 284 +++++++++++++++++++++++++++++++++ e107_web/js/core/all.jquery.js | 10 -- 3 files changed, 295 insertions(+), 10 deletions(-) create mode 100644 e107_handlers/e_ajax_class.php diff --git a/e107_handlers/e107_class.php b/e107_handlers/e107_class.php index 59cf5deab..d11f2b5c1 100644 --- a/e107_handlers/e107_class.php +++ b/e107_handlers/e107_class.php @@ -156,6 +156,7 @@ class e107 'e_admin_request' => '{e_HANDLER}admin_ui.php', 'e_admin_response' => '{e_HANDLER}admin_ui.php', 'e_admin_ui' => '{e_HANDLER}admin_ui.php', + 'e_ajax_class' => '{e_HANDLER}e_ajax_class.php', 'e_array' => '{e_HANDLER}core_functions.php', // Old ArrayStorage. 'e_bbcode' => '{e_HANDLER}bbcode_handler.php', 'e_bb_base' => '{e_HANDLER}bbcode_handler.php', @@ -1655,6 +1656,16 @@ class e107 return self::getSingleton('eMessage', true); } + /** + * Retrieve ajax singleton object + * + * @return e_ajax_class + */ + public static function ajax() + { + return self::getSingleton('e_ajax_class', true); + } + /** * Retrieve Library Manager singleton object (internal use only. Use e107::library()) * diff --git a/e107_handlers/e_ajax_class.php b/e107_handlers/e_ajax_class.php new file mode 100644 index 000000000..0aa55cdc3 --- /dev/null +++ b/e107_handlers/e_ajax_class.php @@ -0,0 +1,284 @@ +commandInvoke('#object-1', 'removeAttr', array('disabled')); + * // Insert HTML content into the '#object-1' element. + * $commands[] = $ajax->commandInsert('#object-1', 'html', 'some html content'); + * + * // This method returns with data in JSON format. It sets the header for + * // JavaScript output. + * $ajax->response($commands); + * @endcode + */ +class e_ajax_class +{ + + /** + * Constructor. + * Use {@link getInstance()}, direct instantiating is not possible for signleton + * objects. + */ + public function __construct() + { + } + + /** + * @return void + */ + protected function _init() + { + } + + /** + * Cloning is not allowed. + */ + private function __clone() + { + } + + /** + * Returns data in JSON format. + * + * This function should be used for JavaScript callback functions returning + * data in JSON format. It sets the header for JavaScript output. + * + * @param $var + * (optional) If set, the variable will be converted to JSON and output. + */ + public function response($var = null) + { + // We are returning JSON, so tell the browser. + header('Content-Type: application/json'); + + if(isset($var)) + { + echo $this->render($var); + } + } + + /** + * Renders a commands array into JSON. + * + * @param array $commands + * A list of macro commands generated by the use of e107::ajax()->command* + * methods. + * + * @return string + */ + public function render($commands = array()) + { + $tp = e107::getParser(); + return $tp->toJSON($commands); + } + + /** + * Creates an Ajax 'alert' command. + * + * The 'alert' command instructs the client to display a JavaScript alert + * dialog box. + * + * @param $text + * The message string to display to the user. + * + * @return array + * An array suitable for use with the e107::ajax->render() function. + */ + public function commandAlert($text) + { + return array( + 'command' => 'alert', + 'text' => $text, + ); + } + + /** + * Creates an Ajax 'insert' command. + * + * This command instructs the client to insert the given HTML. + * + * @param $target + * A jQuery target selector. + * @param $method + * Selected method fo DOM manipulation: + * 'replaceWith', 'append', 'prepend', 'before', 'after', 'html' + * @param $html + * The data to use with the jQuery method. + * + * @return array + * An array suitable for use with the e107::ajax->render() function. + */ + public function commandInsert($target, $method, $html) + { + return array( + 'command' => 'insert', + 'method' => $method, + 'target' => $target, + 'data' => $html, + ); + } + + /** + * Creates an Ajax 'remove' command. + * + * The 'remove' command instructs the client to use jQuery's remove() method + * to remove each of elements matched by the given target, and everything + * within them. + * + * @param $target + * A jQuery selector string. + * + * @return array + * An array suitable for use with the e107::ajax->render() function. + * + * @see http://docs.jquery.com/Manipulation/remove#expr + */ + public function commandRemove($target) + { + return array( + 'command' => 'remove', + 'target' => $target, + ); + } + + /** + * Creates an Ajax 'css' command. + * + * The 'css' command will instruct the client to use the jQuery css() method + * to apply the CSS arguments to elements matched by the given target. + * + * @param $target + * A jQuery selector string. + * @param $argument + * An array of key/value pairs to set in the CSS for the target. + * + * @return array + * An array suitable for use with the e107::ajax->render() function. + * + * @see http://docs.jquery.com/CSS/css#properties + */ + public function commandCSS($target, $argument) + { + return array( + 'command' => 'css', + 'target' => $target, + 'argument' => $argument, + ); + } + + /** + * Creates an Ajax 'settings' command. + * + * The 'settings' command instructs the client to extend e107.settings with + * the given array. + * + * @param $settings + * An array of key/value pairs to add to the settings. This will be utilized + * for all commands after this if they do not include their own settings + * array. + * + * @return array + * An array suitable for use with the e107::ajax->render() function. + */ + public function commandSettings($settings) + { + return array( + 'command' => 'settings', + 'settings' => $settings, + ); + } + + /** + * Creates an Ajax 'data' command. + * + * The 'data' command instructs the client to attach the name=value pair of + * data to the target via jQuery's data cache. + * + * @param $target + * A jQuery selector string. + * @param $name + * The name or key (in the key value pair) of the data attached to this + * target. + * @param $value + * The value of the data. Not just limited to strings can be any format. + * + * @return array + * An array suitable for use with the e107::ajax->render() function. + * + * @see http://docs.jquery.com/Core/data#namevalue + */ + public function commandData($target, $name, $value) + { + return array( + 'command' => 'data', + 'target' => $target, + 'name' => $name, + 'value' => $value, + ); + } + + /** + * Creates an Ajax 'invoke' command. + * + * The 'invoke' command will instruct the client to invoke the given jQuery + * method with the supplied arguments on the elements matched by the given + * target. Intended for simple jQuery commands, such as attr(), addClass(), + * removeClass(), toggleClass(), etc. + * + * @param $target + * A jQuery selector string. + * @param $method + * The jQuery method to invoke. + * @param $arguments + * (optional) A list of arguments to the jQuery $method, if any. + * + * @return array + * An array suitable for use with the e107::ajax->render() function. + */ + public function commandInvoke($target, $method, array $arguments = array()) + { + return array( + 'command' => 'invoke', + 'target' => $target, + 'method' => $method, + 'arguments' => $arguments, + ); + } + +} \ No newline at end of file diff --git a/e107_web/js/core/all.jquery.js b/e107_web/js/core/all.jquery.js index 70f5991f7..46b0766d0 100644 --- a/e107_web/js/core/all.jquery.js +++ b/e107_web/js/core/all.jquery.js @@ -508,16 +508,6 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; var $element = $(command.target); $element[command.method].apply($element, command.arguments); break; - - // Command to set attribute for element. - case 'attr': - $newtarget.attr(command.name, command.value); - break; - - // Command to remove attribute from element. - case 'removeAttr': - $newtarget.removeAttr(command.name); - break; } }); }; From 80cf7652a98c6858325ec038e72e16a13a33b8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Thu, 3 Mar 2016 12:15:01 +0100 Subject: [PATCH 09/11] Fixed PHP docs. --- e107_handlers/e_ajax_class.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/e107_handlers/e_ajax_class.php b/e107_handlers/e_ajax_class.php index 0aa55cdc3..39bdfe506 100644 --- a/e107_handlers/e_ajax_class.php +++ b/e107_handlers/e_ajax_class.php @@ -37,7 +37,9 @@ * $ajax = e107::ajax(); * * $commands = array(); - * // Remove 'disabled' attribute from the content of '#object-1' on the page. + * // Merge array into e107.settings javascript object. + * $commands[] = $ajax->commandSettings(array('foo' => 'bar')); + * // Remove 'disabled' attribute from the '#object-1' element. * $commands[] = $ajax->commandInvoke('#object-1', 'removeAttr', array('disabled')); * // Insert HTML content into the '#object-1' element. * $commands[] = $ajax->commandInsert('#object-1', 'html', 'some html content'); @@ -118,7 +120,7 @@ class e_ajax_class * The message string to display to the user. * * @return array - * An array suitable for use with the e107::ajax->render() function. + * An array suitable for use with the e107::ajax()->render() function. */ public function commandAlert($text) { @@ -142,7 +144,7 @@ class e_ajax_class * The data to use with the jQuery method. * * @return array - * An array suitable for use with the e107::ajax->render() function. + * An array suitable for use with the e107::ajax()->render() function. */ public function commandInsert($target, $method, $html) { @@ -165,7 +167,7 @@ class e_ajax_class * A jQuery selector string. * * @return array - * An array suitable for use with the e107::ajax->render() function. + * An array suitable for use with the e107::ajax()->render() function. * * @see http://docs.jquery.com/Manipulation/remove#expr */ @@ -189,7 +191,7 @@ class e_ajax_class * An array of key/value pairs to set in the CSS for the target. * * @return array - * An array suitable for use with the e107::ajax->render() function. + * An array suitable for use with the e107::ajax()->render() function. * * @see http://docs.jquery.com/CSS/css#properties */ @@ -214,7 +216,7 @@ class e_ajax_class * array. * * @return array - * An array suitable for use with the e107::ajax->render() function. + * An array suitable for use with the e107::ajax()->render() function. */ public function commandSettings($settings) { @@ -239,7 +241,7 @@ class e_ajax_class * The value of the data. Not just limited to strings can be any format. * * @return array - * An array suitable for use with the e107::ajax->render() function. + * An array suitable for use with the e107::ajax()->render() function. * * @see http://docs.jquery.com/Core/data#namevalue */ @@ -269,7 +271,7 @@ class e_ajax_class * (optional) A list of arguments to the jQuery $method, if any. * * @return array - * An array suitable for use with the e107::ajax->render() function. + * An array suitable for use with the e107::ajax()->render() function. */ public function commandInvoke($target, $method, array $arguments = array()) { From 3a1d17e6a272289e5360e2e8e6dc06047d4e9f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Mon, 7 Mar 2016 13:42:58 +0100 Subject: [PATCH 10/11] Fixed missing target issue. --- e107_web/js/core/all.jquery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e107_web/js/core/all.jquery.js b/e107_web/js/core/all.jquery.js index 46b0766d0..6e85c635a 100644 --- a/e107_web/js/core/all.jquery.js +++ b/e107_web/js/core/all.jquery.js @@ -397,7 +397,7 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}}; } // BC. - if(options.target.charAt(0) != "#" && options.target.charAt(0) != ".") + if(options.target && options.target.charAt(0) != "#" && options.target.charAt(0) != ".") { options.target = "#" + options.target; } From ceed9a02a78334b64636eebfb402d0b2b4e7c640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3na=20Lore?= Date: Mon, 7 Mar 2016 22:21:19 +0100 Subject: [PATCH 11/11] Change method ajax() to getAjax(). --- e107_handlers/e107_class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e107_handlers/e107_class.php b/e107_handlers/e107_class.php index d11f2b5c1..b54b48e4d 100644 --- a/e107_handlers/e107_class.php +++ b/e107_handlers/e107_class.php @@ -1661,7 +1661,7 @@ class e107 * * @return e_ajax_class */ - public static function ajax() + public static function getAjax() { return self::getSingleton('e_ajax_class', true); }