Commit 3b2d24af authored by webchick's avatar webchick

#561726 by effulgentsia, TwoD, and sun: Make ajax.js and tabledrag.js...

#561726 by effulgentsia, TwoD, and sun: Make ajax.js and tabledrag.js implement Drupal.detachBehaviors().
parent 112e0ffa
...@@ -120,6 +120,9 @@ Drupal.ajax = function (base, element, element_settings) { ...@@ -120,6 +120,9 @@ Drupal.ajax = function (base, element, element_settings) {
var options = { var options = {
url: ajax.url, url: ajax.url,
data: ajax.button, data: ajax.button,
beforeSerialize: function (element_settings, options) {
return ajax.beforeSerialize(element_settings, options);
},
beforeSubmit: function (form_values, element_settings, options) { beforeSubmit: function (form_values, element_settings, options) {
return ajax.beforeSubmit(form_values, element_settings, options); return ajax.beforeSubmit(form_values, element_settings, options);
}, },
...@@ -176,6 +179,18 @@ Drupal.ajax = function (base, element, element_settings) { ...@@ -176,6 +179,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. * Handler for the form redirection submission.
*/ */
...@@ -230,6 +245,13 @@ Drupal.ajax.prototype.success = function (response, status) { ...@@ -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(); Drupal.unfreezeHeight();
// Remove any response-specific settings so they don't get used on the next // 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) { ...@@ -280,6 +302,9 @@ Drupal.ajax.prototype.error = function (response, uri) {
$(this.wrapper).show(); $(this.wrapper).show();
// Re-enable the element. // Re-enable the element.
$(this.element).removeClass('progress-disabled').attr('disabled', false); $(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 = { ...@@ -300,6 +325,17 @@ Drupal.ajax.prototype.commands = {
// Safari with long string lengths. http://dev.jquery.com/ticket/3178 // Safari with long string lengths. http://dev.jquery.com/ticket/3178
var new_content = $('<div></div>').html(response.data); 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. // Add the new content to the page.
wrapper[method](new_content); wrapper[method](new_content);
...@@ -333,6 +369,8 @@ Drupal.ajax.prototype.commands = { ...@@ -333,6 +369,8 @@ Drupal.ajax.prototype.commands = {
* Command to remove a chunk from the page. * Command to remove a chunk from the page.
*/ */
remove: function (ajax, response, status) { remove: function (ajax, response, status) {
var settings = response.settings || ajax.settings || Drupal.settings;
Drupal.detachBehaviors($(response.selector), settings);
$(response.selector).remove(); $(response.selector).remove();
}, },
......
...@@ -22,10 +22,10 @@ if ($ === undefined) { ...@@ -22,10 +22,10 @@ if ($ === undefined) {
* object using the method 'attach' and optionally also 'detach' as follows: * object using the method 'attach' and optionally also 'detach' as follows:
* @code * @code
* Drupal.behaviors.behaviorName = { * 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) { ...@@ -81,16 +81,38 @@ Drupal.attachBehaviors = function (context, settings) {
* @param context * @param context
* An element to detach behaviors from. If none is given, the document element * An element to detach behaviors from. If none is given, the document element
* is used. * 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 * @see Drupal.attachBehaviors
*/ */
Drupal.detachBehaviors = function (context, settings) { Drupal.detachBehaviors = function (context, settings, trigger) {
context = context || document; context = context || document;
settings = settings || Drupal.settings; settings = settings || Drupal.settings;
trigger = trigger || 'unload';
// Execute all of them. // Execute all of them.
$.each(Drupal.behaviors, function () { $.each(Drupal.behaviors, function () {
if ($.isFunction(this.detach)) { if ($.isFunction(this.detach)) {
this.detach(context, settings); this.detach(context, settings, trigger);
} }
}); });
}; };
......
...@@ -903,7 +903,9 @@ Drupal.tableDrag.prototype.row.prototype.isValidSwap = function (row) { ...@@ -903,7 +903,9 @@ Drupal.tableDrag.prototype.row.prototype.isValidSwap = function (row) {
* DOM element what will be swapped with the row group. * DOM element what will be swapped with the row group.
*/ */
Drupal.tableDrag.prototype.row.prototype.swap = function (position, row) { Drupal.tableDrag.prototype.row.prototype.swap = function (position, row) {
Drupal.detachBehaviors(this.group, Drupal.settings, 'move');
$(row)[position](this.group); $(row)[position](this.group);
Drupal.attachBehaviors(this.group, Drupal.settings);
this.changed = true; this.changed = true;
this.onSwap(row); this.onSwap(row);
}; };
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment