1
0
mirror of https://github.com/e107inc/e107.git synced 2025-07-07 00:05:00 +02:00

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...

This commit is contained in:
Lóna Lore
2016-03-02 15:12:34 +01:00
parent 8b83eeb914
commit 24cc54caf6

View File

@ -6,6 +6,8 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}};
(function ($) { (function ($) {
e107.ajax = e107.ajax || {};
e107.callbacks = e107.callbacks || {}; e107.callbacks = e107.callbacks || {};
/** /**
@ -117,162 +119,114 @@ 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 = { e107.ajax.getDefaultEventHandler = function ($element)
attach: function (context, settings)
{ {
$(context).find('a.e-ajax').once('e-ajax-link').each(function () var event = 'click'; // Default event handler.
{ var tag = $element.prop("tagName").toLowerCase();
$(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');
if(nav != null) if(tag == 'input')
{ {
// Modify data-src value for next/prev. 'from=' var type = $element.attr('type').toLowerCase();
e107.callbacks.eNav(this, '.e-ajax');
}
// URL for Ajax request. switch(type)
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 = $("<img src='" + loading + "' alt='' class='e-ajax-progress' />"); case 'submit':
$(this).after($loadingImage); 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
if(target == null || handler == null) // Old way - href='myscript.php#id-to-target // leads to problems, like breaking autocomplete textfields, so we bind
{ // to mousedown instead of click.
if(href != null) event = 'mousedown';
{
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; break;
case 'append': case 'radio':
html = $.parseHTML(data); case 'checkbox':
$target.append(html); event = 'change';
break; break;
case 'prepend': // text, number, password, date, datetime, datetime-local, month, week, time,
html = $.parseHTML(data); // email, search, tel, url, color, range
$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: default:
$target.html(data).hide().show("slow"); event = 'blur';
break; break;
} }
// Attach all registered behaviors to the new content.
e107.attachBehaviors();
} }
}); 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;
return false; 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)
attach: function (context, settings)
{ {
$(context).find('select.e-ajax').once('e-ajax-select').each(function ()
{
$(this).on('change', function ()
{
var form = $(this).closest("form").attr('id');
// 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 data = $('#' + form).serialize();
var $target = $("#" + target);
var html = null;
var $loadingImage = null; var $loadingImage = null;
// TODO: set default loading icon? // Loading image.
if(loading != null) if(options.loading != null)
{ {
$loadingImage = $("<img src='" + loading + "' alt='' class='e-ajax-progress' />"); $loadingImage = $(options.loading);
$(this).after($loadingImage); $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({ $.ajax({
type: 'post', type: options.type || 'POST',
url: handler, url: options.url,
data: data, data: data || '',
complete: function () complete: function ()
{ {
if($loadingImage) if($loadingImage)
@ -280,9 +234,123 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}};
$loadingImage.remove(); $loadingImage.remove();
} }
}, },
success: function (data) success: function (response)
{ {
switch(method) 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': case 'replaceWith':
html = $.parseHTML(data); html = $.parseHTML(data);
@ -317,8 +385,53 @@ var e107 = e107 || {'settings': {}, 'behaviors': {}};
// Attach all registered behaviors to the new content. // Attach all registered behaviors to the new content.
e107.attachBehaviors(); 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('.e-ajax').once('e-ajax').each(function ()
{
var $this = $(this);
var event = $this.attr('data-event') || e107.ajax.getDefaultEventHandler($this);
$this.on(event, function ()
{
var $element = $(this);
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")
};
// If this is a navigation controller, e.g. pager.
if(ajaxOptions.nav != null)
{
// 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';
} }
});
e107.ajax.ajaxRequestHandler($element, ajaxOptions);
return false; return false;
}); });