diff options
author | Angie Byron <webchick@24967.no-reply.drupal.org> | 2009-08-17 07:12:16 +0000 |
---|---|---|
committer | Angie Byron <webchick@24967.no-reply.drupal.org> | 2009-08-17 07:12:16 +0000 |
commit | 25b9f686a626fc5424c36555fe4757cb8114d4ea (patch) | |
tree | 8640840732e7b9ebcdfaf15a37054714696d4c0f /misc | |
parent | 55d9463766e08ebaf9aa08955a418e18fd889353 (diff) | |
download | brdo-25b9f686a626fc5424c36555fe4757cb8114d4ea.tar.gz brdo-25b9f686a626fc5424c36555fe4757cb8114d4ea.tar.bz2 |
#544418 by merlinofchaos, sun, drewish, quicksketch, et al: Integrate CTools AJAX framework with Drupal to extend (and replace) existing ahah framework. Everything about AJAX/AHAH is more betterer now.
Diffstat (limited to 'misc')
-rw-r--r-- | misc/ahah.js | 259 | ||||
-rw-r--r-- | misc/ajax.js | 383 | ||||
-rw-r--r-- | misc/autocomplete.js | 2 | ||||
-rw-r--r-- | misc/drupal.js | 4 | ||||
-rw-r--r-- | misc/progress.js | 2 |
5 files changed, 387 insertions, 263 deletions
diff --git a/misc/ahah.js b/misc/ahah.js deleted file mode 100644 index 84ff07082..000000000 --- a/misc/ahah.js +++ /dev/null @@ -1,259 +0,0 @@ -// $Id$ -(function ($) { - -/** - * Provides AJAX-like page updating via AHAH (Asynchronous HTML and HTTP). - * - * AHAH is a method of making a request via Javascript while viewing an HTML - * page. The request returns a small chunk of HTML, which is then directly - * injected into the page. - * - * Drupal uses this file to enhance form elements with #ahah[path] and - * #ahah[wrapper] properties. If set, this file will automatically be included - * to provide AHAH capabilities. - */ - -Drupal.ahah = Drupal.ahah || {}; - -/** - * Attaches the ahah behavior to each ahah form element. - */ -Drupal.behaviors.ahah = { - attach: function (context, settings) { - for (var base in settings.ahah) { - if (!$('#' + base + '.ahah-processed').size()) { - var element_settings = settings.ahah[base]; - - $(element_settings.selector).each(function () { - element_settings.element = this; - Drupal.ahah[base] = new Drupal.ahah(base, element_settings); - }); - - $('#' + base).addClass('ahah-processed'); - } - } - } -}; - -/** - * AHAH object. - * - * All AHAH objects on a page are accessible through the global Drupal.ahah object - * and are keyed by the submit button's ID. You can access them from your module's - * JavaScript file to override properties or functions. - * For example, if your AHAH enabled button has the ID 'edit-submit', you can - * redefine the function that is called to insert the new content like this - * (inside a Drupal.behaviors attach block): - * @code - * Drupal.behaviors.myCustomAhahStuff = { - * attach: function(context, settings) { - * Drupal.ahah['edit-submit'].insertNewContent = function(response, status) { - * new_content = $(response.data); - * $('#my-wrapper').append(new_content); - * alert('New content was appended to #my-wrapper'); - * } - * } - * }; - * @endcode - */ -Drupal.ahah = function (base, element_settings) { - // Set the properties for this object. - this.element = element_settings.element; - this.selector = element_settings.selector; - this.event = element_settings.event; - this.keypress = element_settings.keypress; - this.url = element_settings.url; - this.wrapper = '#' + element_settings.wrapper; - this.effect = element_settings.effect; - this.method = element_settings.method; - this.progress = element_settings.progress; - this.button = element_settings.button || { }; - - if (this.effect == 'none') { - this.showEffect = 'show'; - this.hideEffect = 'hide'; - this.showSpeed = ''; - } - else if (this.effect == 'fade') { - this.showEffect = 'fadeIn'; - this.hideEffect = 'fadeOut'; - this.showSpeed = 'slow'; - } - else { - this.showEffect = this.effect + 'Toggle'; - this.hideEffect = this.effect + 'Toggle'; - this.showSpeed = 'slow'; - } - - // Record the form action and target, needed for iFrame file uploads. - var form = $(this.element).parents('form'); - this.form_action = form.attr('action'); - this.form_target = form.attr('target'); - this.form_encattr = form.attr('encattr'); - - // Set the options for the ajaxSubmit function. - // The 'this' variable will not persist inside of the options object. - var ahah = this; - var options = { - url: ahah.url, - data: ahah.button, - beforeSubmit: function (form_values, element_settings, options) { - return ahah.beforeSubmit(form_values, element_settings, options); - }, - success: function (response, status) { - // Sanity check for browser support (object expected). - // When using iFrame uploads, responses must be returned as a string. - if (typeof response == 'string') { - response = Drupal.parseJson(response); - } - return ahah.success(response, status); - }, - complete: function (response, status) { - if (status == 'error' || status == 'parsererror') { - return ahah.error(response, ahah.url); - } - }, - dataType: 'json', - type: 'POST' - }; - - // Bind the ajaxSubmit function to the element event. - $(element_settings.element).bind(element_settings.event, function () { - $(element_settings.element).parents('form').ajaxSubmit(options); - return false; - }); - // If necessary, enable keyboard submission so that AHAH behaviors - // can be triggered through keyboard input as well as e.g. a mousedown - // action. - if (element_settings.keypress) { - $(element_settings.element).keypress(function (event) { - // Detect enter key. - if (event.keyCode == 13) { - $(element_settings.element).trigger(element_settings.event); - return false; - } - }); - } -}; - -/** - * Handler for the form redirection submission. - */ -Drupal.ahah.prototype.beforeSubmit = function (form_values, element, options) { - // Disable the element that received the change. - $(this.element).addClass('progress-disabled').attr('disabled', true); - - // Insert progressbar or throbber. - if (this.progress.type == 'bar') { - var progressBar = new Drupal.progressBar('ahah-progress-' + this.element.id, eval(this.progress.update_callback), this.progress.method, eval(this.progress.error_callback)); - if (this.progress.message) { - progressBar.setProgress(-1, this.progress.message); - } - if (this.progress.url) { - progressBar.startMonitoring(this.progress.url, this.progress.interval || 1500); - } - this.progress.element = $(progressBar.element).addClass('ahah-progress ahah-progress-bar'); - this.progress.object = progressBar; - $(this.element).after(this.progress.element); - } - else if (this.progress.type == 'throbber') { - this.progress.element = $('<div class="ahah-progress ahah-progress-throbber"><div class="throbber"> </div></div>'); - if (this.progress.message) { - $('.throbber', this.progress.element).after('<div class="message">' + this.progress.message + '</div>'); - } - $(this.element).after(this.progress.element); - } -}; - -/** - * Handler for the form redirection completion. - */ -Drupal.ahah.prototype.success = function (response, status) { - var form = $(this.element).parents('form'); - - // Restore the previous action and target to the form. - form.attr('action', this.form_action); - this.form_target ? form.attr('target', this.form_target) : form.removeAttr('target'); - this.form_encattr ? form.attr('target', this.form_encattr) : form.removeAttr('encattr'); - - // Remove the progress element. - if (this.progress.element) { - $(this.progress.element).remove(); - } - if (this.progress.object) { - this.progress.object.stopMonitoring(); - } - $(this.element).removeClass('progress-disabled').attr('disabled', false); - - Drupal.freezeHeight(); - - // Call the insertNewContent handler to insert the new content into the page. - this.insertNewContent(response, status); - - Drupal.unfreezeHeight(); -}; - -/** - * Handler to insert the new content into the page. - */ -Drupal.ahah.prototype.insertNewContent = function (response, status) { - var wrapper = $(this.wrapper); - - // Manually insert HTML into the jQuery object, using $() directly crashes - // Safari with long string lengths. http://dev.jquery.com/ticket/1152 - var new_content = $('<div></div>').html(response.data); - - // Add the new content to the page. - if (this.method == 'replace') { - wrapper.empty().append(new_content); - } - else { - wrapper[this.method](new_content); - } - - // Immediately hide the new content if we're using any effects. - if (this.showEffect != 'show') { - new_content.hide(); - } - - // Determine what effect use and what content will receive the effect, then - // show the new content. - if ($('.ahah-new-content', new_content).size() > 0) { - $('.ahah-new-content', new_content).hide(); - new_content.show(); - $('.ahah-new-content', new_content)[this.showEffect](this.showSpeed); - } - else if (this.showEffect != 'show') { - new_content[this.showEffect](this.showSpeed); - } - - // Attach all javascript behaviors to the new content, if it was successfully - // added to the page, this if statement allows #ahah[wrapper] to be optional. - if (new_content.parents('html').length > 0) { - // Apply any settings from the returned JSON if available. - var settings = response.settings || Drupal.settings; - Drupal.attachBehaviors(new_content, settings); - } -}; - -/** - * Handler for the form redirection error. - */ -Drupal.ahah.prototype.error = function (response, uri) { - alert(Drupal.ahahError(response, uri)); - // Resore the previous action and target to the form. - $(this.element).parent('form').attr({ action: this.form_action, target: this.form_target }); - // Remove the progress element. - if (this.progress.element) { - $(this.progress.element).remove(); - } - if (this.progress.object) { - this.progress.object.stopMonitoring(); - } - // Undo hide. - $(this.wrapper).show(); - // Re-enable the element. - $(this.element).removeClass('progress-disabled').attr('disabled', false); -}; - -})(jQuery); diff --git a/misc/ajax.js b/misc/ajax.js new file mode 100644 index 000000000..953827f1d --- /dev/null +++ b/misc/ajax.js @@ -0,0 +1,383 @@ +// $Id$ +(function ($) { + +/** + * Provides AJAX page updating via jQuery $.ajax (Asynchronous JavaScript and XML). + * + * AJAX is a method of making a request via Javascript while viewing an HTML + * page. The request returns an array of commands encoded in JSON, which is + * then executed to make any changes that are necessary to the page. + * + * Drupal uses this file to enhance form elements with #ajax['path'] and + * #ajax['wrapper'] properties. If set, this file will automatically be included + * to provide AJAX capabilities. + */ + +Drupal.ajax = Drupal.ajax || {}; + +/** + * Attaches the AJAX behavior to each AJAX form element. + */ +Drupal.behaviors.AJAX = { + attach: function (context, settings) { + // Load all AJAX behaviors specified in the settings. + for (var base in settings.ajax) { + if (!$('#' + base + '.ajax-processed').length) { + var element_settings = settings.ajax[base]; + + $(element_settings.selector).each(function () { + Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); + }); + + $('#' + base).addClass('ajax-processed'); + } + } + + // Bind AJAX behaviors to all items showing the class. + $('.use-ajax:not(.ajax-processed)').addClass('ajax-processed').each(function () { + var element_settings = {}; + + // For anchor tags, these will go to the target of the anchor rather + // than the usual location. + if ($(this).attr('href')) { + element_settings.url = $(this).attr('href'); + } + var base = $(this).attr('id'); + Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); + }); + + // This class means to submit the form to the action using AJAX. + $('.use-ajax-submit:not(.ajax-processed)').addClass('ajax-processed').each(function () { + var element_settings = {}; + + // AJAX submits specified in this manner automatically submit to the + // normal form action. + element_settings.url = $(this.form).attr('action'); + element_settings.set_click = TRUE; + + var base = $(this).attr('id'); + Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); + }); + } +}; + +/** + * AJAX object. + * + * All AJAX objects on a page are accessible through the global Drupal.ajax + * object and are keyed by the submit button's ID. You can access them from + * your module's JavaScript file to override properties or functions. + * + * For example, if your AJAX enabled button has the ID 'edit-submit', you can + * redefine the function that is called to insert the new content like this + * (inside a Drupal.behaviors attach block): + * @code + * Drupal.behaviors.myCustomAJAXStuff = { + * attach: function (context, settings) { + * Drupal.ajax['edit-submit'].commands.insert = function (ajax, response, status) { + * new_content = $(response.data); + * $('#my-wrapper').append(new_content); + * alert('New content was appended to #my-wrapper'); + * } + * } + * }; + * @endcode + */ +Drupal.ajax = function (base, element, element_settings) { + var defaults = { + url: 'system/ajax', + event: 'mousedown', + keypress: true, + selector: '#' + base, + effect: 'none', + speed: 'slow', + method: 'replace', + progress: { + type: 'bar', + message: 'Please wait...' + }, + button: {}, + }; + + $.extend(this, defaults, element_settings); + + this.element = element; + + // Replacing 'nojs' with 'ajax' in the URL allows for an easy method to let + // the server detect when it needs to degrade gracefully. + this.url = element_settings.url.replace('/nojs/', '/ajax/'); + this.wrapper = '#' + element_settings.wrapper; + + // If there isn't a form, jQuery.ajax() will be used instead, allowing us to + // bind AJAX to links as well. + if (this.element.form) { + this.form = $(this.element.form); + } + + // Set the options for the ajaxSubmit function. + // The 'this' variable will not persist inside of the options object. + var ajax = this; + var options = { + url: ajax.url, + data: ajax.button, + beforeSubmit: function (form_values, element_settings, options) { + return ajax.beforeSubmit(form_values, element_settings, options); + }, + success: function (response, status) { + // Sanity check for browser support (object expected). + // When using iFrame uploads, responses must be returned as a string. + if (typeof response == 'string') { + response = Drupal.parseJson(response); + } + return ajax.success(response, status); + }, + complete: function (response, status) { + if (status == 'error' || status == 'parsererror') { + return ajax.error(response, ajax.url); + } + }, + dataType: 'json', + type: 'POST' + }; + + // Bind the ajaxSubmit function to the element event. + $(this.element).bind(element_settings.event, function () { + if (ajax.form) { + // If setClick is set, we must set this to ensure that the button's + // value is passed. + if (ajax.setClick) { + // Mark the clicked button. 'form.clk' is a special variable for + // ajaxSubmit that tells the system which element got clicked to + // trigger the submit. Without it there would be no 'op' or + // equivalent. + ajax.form.clk = this.element; + } + + ajax.form.ajaxSubmit(options); + } + else { + $.ajax(options); + } + + return false; + }); + + // If necessary, enable keyboard submission so that AJAX behaviors + // can be triggered through keyboard input as well as e.g. a mousedown + // action. + if (element_settings.keypress) { + $(element_settings.element).keypress(function (event) { + // Detect enter key. + if (event.keyCode == 13) { + $(element_settings.element).trigger(element_settings.event); + return false; + } + }); + } +}; + +/** + * Handler for the form redirection submission. + */ +Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) { + // Disable the element that received the change. + $(this.element).addClass('progress-disabled').attr('disabled', true); + + // Insert progressbar or throbber. + if (this.progress.type == 'bar') { + var progressBar = new Drupal.progressBar('ajax-progress-' + this.element.id, eval(this.progress.update_callback), this.progress.method, eval(this.progress.error_callback)); + if (this.progress.message) { + progressBar.setProgress(-1, this.progress.message); + } + if (this.progress.url) { + progressBar.startMonitoring(this.progress.url, this.progress.interval || 1500); + } + this.progress.element = $(progressBar.element).addClass('ajax-progress ajax-progress-bar'); + this.progress.object = progressBar; + $(this.element).after(this.progress.element); + } + else if (this.progress.type == 'throbber') { + this.progress.element = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber"> </div></div>'); + if (this.progress.message) { + $('.throbber', this.progress.element).after('<div class="message">' + this.progress.message + '</div>'); + } + $(this.element).after(this.progress.element); + } +}; + +/** + * Handler for the form redirection completion. + */ +Drupal.ajax.prototype.success = function (response, status) { + // Remove the progress element. + if (this.progress.element) { + $(this.progress.element).remove(); + } + if (this.progress.object) { + this.progress.object.stopMonitoring(); + } + $(this.element).removeClass('progress-disabled').attr('disabled', false); + + Drupal.freezeHeight(); + + for (i in response) { + if (response[i]['command'] && this.commands[response[i]['command']]) { + this.commands[response[i]['command']](this, response[i], status); + } + } + + Drupal.unfreezeHeight(); + + // Remove any response-specific settings so they don't get used on the next + // call by mistake. + this.settings = {}; +}; + +/** + * Build an effect object which tells us how to apply the effect when adding new HTML. + */ +Drupal.ajax.prototype.getEffect = function (response) { + var type = response.effect || this.effect; + var speed = response.speed || this.speed; + + var effect = {}; + if (type == 'none') { + effect.showEffect = 'show'; + effect.hideEffect = 'hide'; + effect.showSpeed = ''; + } + else if (type == 'fade') { + effect.showEffect = 'fadeIn'; + effect.hideEffect = 'fadeOut'; + effect.showSpeed = speed; + } + else { + effect.showEffect = type + 'Toggle'; + effect.hideEffect = type + 'Toggle'; + effect.showSpeed = speed; + } + + return effect; +} + +/** + * Handler for the form redirection error. + */ +Drupal.ajax.prototype.error = function (response, uri) { + alert(Drupal.ajaxError(response, uri)); + // Remove the progress element. + if (this.progress.element) { + $(this.progress.element).remove(); + } + if (this.progress.object) { + this.progress.object.stopMonitoring(); + } + // Undo hide. + $(this.wrapper).show(); + // Re-enable the element. + $(this.element).removeClass('progress-disabled').attr('disabled', false); +}; + +/** + * Provide a series of commands that the server can request the client perform. + */ +Drupal.ajax.prototype.commands = { + /** + * Command to insert new content into the DOM. + */ + insert: function (ajax, response, status) { + // Get information from the response. If it is not there, default to + // our presets. + var wrapper = response.selector ? $(response.selector) : $(ajax.wrapper); + var method = response.method || ajax.method; + var effect = ajax.getEffect(response); + + // Manually insert HTML into the jQuery object, using $() directly crashes + // Safari with long string lengths. http://dev.jquery.com/ticket/3178 + var new_content = $('<div></div>').html(response.data); + + // Add the new content to the page. + wrapper[method](new_content); + + // Immediately hide the new content if we're using any effects. + if (effect.showEffect != 'show') { + new_content.hide(); + } + + // Determine which effect to use and what content will receive the + // effect, then show the new content. + if ($('.ajax-new-content', new_content).length > 0) { + $('.ajax-new-content', new_content).hide(); + new_content.show(); + $('.ajax-new-content', new_content)[effect.showEffect](effect.showSpeed); + } + else if (effect.showEffect != 'show') { + new_content[effect.showEffect](effect.showSpeed); + } + + // Attach all JavaScript behaviors to the new content, if it was successfully + // added to the page, this if statement allows #ajax['wrapper'] to be + // optional. + if (new_content.parents('html').length > 0) { + // Apply any settings from the returned JSON if available. + var settings = response.settings || ajax.settings || Drupal.settings; + Drupal.attachBehaviors(new_content, settings); + } + }, + + /** + * Command to remove a chunk from the page. + */ + remove: function (ajax, response, status) { + $(response.selector).remove(); + }, + + /** + * Command to mark a chunk changed. + */ + changed: function (ajax, response, status) { + if (!$(response.selector).hasClass('ajax-changed')) { + $(response.selector).addClass('ajax-changed'); + if (response.asterisk) { + $(response.selector).find(response.asterisk).append(' <span class="ajax-changed">*</span> '); + } + } + }, + + /** + * Command to provide an alert. + */ + alert: function (ajax, response, status) { + alert(response.text, response.title); + }, + + /** + * Command to set the settings that will be used for other commands in this response. + */ + settings: function (ajax, response, status) { + ajax.settings = response.settings; + }, + + /** + * Command to attach data using jQuery's data API. + */ + data: function (ajax, response, status) { + $(response.selector).data(response.name, response.value); + }, + + /** + * Command to restripe a table. + */ + restripe: function (ajax, response, status) { + // :even and :odd are reversed because jQuery counts from 0 and + // we count from 1, so we're out of sync. + $('tbody tr:not(:hidden)', $(response.selector)) + .removeClass('even').removeClass('odd') + .filter(':even') + .addClass('odd').end() + .filter(':odd') + .addClass('even'); + } +}; + +})(jQuery); diff --git a/misc/autocomplete.js b/misc/autocomplete.js index bed9b65ae..87e724d46 100644 --- a/misc/autocomplete.js +++ b/misc/autocomplete.js @@ -289,7 +289,7 @@ Drupal.ACDB.prototype.search = function (searchString) { } }, error: function (xmlhttp) { - alert(Drupal.ahahError(xmlhttp, db.uri)); + alert(Drupal.ajaxError(xmlhttp, db.uri)); } }); }, this.delay); diff --git a/misc/drupal.js b/misc/drupal.js index 40a21b0f1..dbd057bb9 100644 --- a/misc/drupal.js +++ b/misc/drupal.js @@ -294,9 +294,9 @@ Drupal.getSelection = function (element) { }; /** - * Build an error message from ahah response. + * Build an error message from an AJAX response. */ -Drupal.ahahError = function (xmlhttp, uri) { +Drupal.ajaxError = function (xmlhttp, uri) { if (xmlhttp.status == 200 || (xmlhttp.status == 500 && xmlhttp.statusText == 'Service unavailable (with message)')) { if ($.trim(xmlhttp.responseText)) { var message = Drupal.t("An error occurred. \nPath: @uri\nMessage: !text", { '@uri': uri, '!text': xmlhttp.responseText }); diff --git a/misc/progress.js b/misc/progress.js index 2d95573c2..36b297fad 100644 --- a/misc/progress.js +++ b/misc/progress.js @@ -84,7 +84,7 @@ Drupal.progressBar.prototype.sendPing = function () { pb.timer = setTimeout(function () { pb.sendPing(); }, pb.delay); }, error: function (xmlhttp) { - pb.displayError(Drupal.ahahError(xmlhttp, pb.uri)); + pb.displayError(Drupal.ajaxError(xmlhttp, pb.uri)); } }); } |