diff options
-rw-r--r-- | misc/ajax.js | 38 | ||||
-rw-r--r-- | misc/drupal.js | 30 | ||||
-rw-r--r-- | misc/tabledrag.js | 2 |
3 files changed, 66 insertions, 4 deletions
diff --git a/misc/ajax.js b/misc/ajax.js index 79217dfd6..0daa9bc4e 100644 --- a/misc/ajax.js +++ b/misc/ajax.js @@ -120,6 +120,9 @@ Drupal.ajax = function (base, element, element_settings) { var options = { url: ajax.url, data: ajax.button, + beforeSerialize: function (element_settings, options) { + return ajax.beforeSerialize(element_settings, options); + }, beforeSubmit: function (form_values, element_settings, options) { return ajax.beforeSubmit(form_values, element_settings, options); }, @@ -177,6 +180,18 @@ Drupal.ajax = function (base, element, element_settings) { }; /** + * Handler for the form serialization. + * + * Runs before the beforeSubmit() handler (see below), and unlike that one, runs + * before field data is collected. + */ +Drupal.ajax.prototype.beforeSerialize = function (element, options) { + // Allow detaching behaviors to update field values before collecting them. + var settings = this.settings || Drupal.settings; + Drupal.detachBehaviors(this.form, settings, 'serialize'); +}; + +/** * Handler for the form redirection submission. */ Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) { @@ -230,6 +245,13 @@ Drupal.ajax.prototype.success = function (response, status) { } } + // Reattach behaviors that were detached in beforeSerialize(). The + // attachBehaviors() called on the new content from processing the response + // commands is not sufficient, because behaviors from the entire form need + // to be reattached. + var settings = this.settings || Drupal.settings; + Drupal.attachBehaviors(this.form, settings); + Drupal.unfreezeHeight(); // Remove any response-specific settings so they don't get used on the next @@ -280,6 +302,9 @@ Drupal.ajax.prototype.error = function (response, uri) { $(this.wrapper).show(); // Re-enable the element. $(this.element).removeClass('progress-disabled').attr('disabled', false); + // Reattach behaviors that were detached in beforeSerialize(). + var settings = response.settings || this.settings || Drupal.settings; + Drupal.attachBehaviors(this.form, settings); }; /** @@ -300,6 +325,17 @@ Drupal.ajax.prototype.commands = { // Safari with long string lengths. http://dev.jquery.com/ticket/3178 var new_content = $('<div></div>').html(response.data); + // If removing content from the wrapper, detach behaviors first. + switch (method) { + case 'html': + case 'replaceWith': + case 'replaceAll': + case 'empty': + case 'remove': + var settings = response.settings || ajax.settings || Drupal.settings; + Drupal.detachBehaviors(wrapper, settings); + } + // Add the new content to the page. wrapper[method](new_content); @@ -333,6 +369,8 @@ Drupal.ajax.prototype.commands = { * Command to remove a chunk from the page. */ remove: function (ajax, response, status) { + var settings = response.settings || ajax.settings || Drupal.settings; + Drupal.detachBehaviors($(response.selector), settings); $(response.selector).remove(); }, diff --git a/misc/drupal.js b/misc/drupal.js index 87a5972c6..e3a48d498 100644 --- a/misc/drupal.js +++ b/misc/drupal.js @@ -22,10 +22,10 @@ if ($ === undefined) { * object using the method 'attach' and optionally also 'detach' as follows: * @code * Drupal.behaviors.behaviorName = { - * attach: function (context) { + * attach: function (context, settings) { * ... * }, - * detach: function (context) { + * detach: function (context, settings, trigger) { * ... * } * }; @@ -81,16 +81,38 @@ Drupal.attachBehaviors = function (context, settings) { * @param context * An element to detach behaviors from. If none is given, the document element * is used. + * @param settings + * An object containing settings for the current context. If none given, the + * global Drupal.settings object is used. + * @param trigger + * A string containing what's causing the behaviors to be detached. The + * possible triggers are: + * - unload: (default) The context element is being removed from the DOM. + * - move: The element is about to be moved within the DOM (for example, + * during a tabledrag row swap). After the move is completed, + * Drupal.attachBehaviors() is called, so that the behavior can undo + * whatever it did in response to the move. Many behaviors won't need to + * do anything simply in response to the element being moved, but because + * IFRAME elements reload their "src" when being moved within the DOM, + * behaviors bound to IFRAME elements (like WYSIWYG editors) may need to + * take some action. + * - serialize: When an AJAX form is submitted, this is called with the + * form as the context. This provides every behavior within the form an + * opportunity to ensure that the field elements have correct content + * in them before the form is serialized. The canonical use-case is so + * that WYSIWYG editors can update the hidden textarea to which they are + * bound. * * @see Drupal.attachBehaviors */ -Drupal.detachBehaviors = function (context, settings) { +Drupal.detachBehaviors = function (context, settings, trigger) { context = context || document; settings = settings || Drupal.settings; + trigger = trigger || 'unload'; // Execute all of them. $.each(Drupal.behaviors, function () { if ($.isFunction(this.detach)) { - this.detach(context, settings); + this.detach(context, settings, trigger); } }); }; diff --git a/misc/tabledrag.js b/misc/tabledrag.js index d79eda06c..b40f95300 100644 --- a/misc/tabledrag.js +++ b/misc/tabledrag.js @@ -903,7 +903,9 @@ Drupal.tableDrag.prototype.row.prototype.isValidSwap = function (row) { * DOM element what will be swapped with the row group. */ Drupal.tableDrag.prototype.row.prototype.swap = function (position, row) { + Drupal.detachBehaviors(this.group, Drupal.settings, 'move'); $(row)[position](this.group); + Drupal.attachBehaviors(this.group, Drupal.settings); this.changed = true; this.onSwap(row); }; |