summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2009-08-31 05:51:08 +0000
committerDries Buytaert <dries@buytaert.net>2009-08-31 05:51:08 +0000
commite6e29ac1b0d6780241ced3d5ebcb0558e219e468 (patch)
treea6bdbf6b69ab26e54295d8063e7d1c839cd42f3a
parent41dca3c4e0ec3d355977735f74af2e49ea0eedd7 (diff)
downloadbrdo-e6e29ac1b0d6780241ced3d5ebcb0558e219e468.tar.gz
brdo-e6e29ac1b0d6780241ced3d5ebcb0558e219e468.tar.bz2
- Patch #444344 by kkaefer, sun, Rob Loach: this change introduces a jQuery .once() method which streamlines the way behavior functions work. Previously, we had to manually ensure that an element is only initialized once. Usually, this happens by adding classes and selecting only those elements which do not have that class. However, this process can be separated out into a jQuery ‘filtering’ function which does all the grunt work.
-rw-r--r--includes/common.inc3
-rw-r--r--misc/autocomplete.js3
-rw-r--r--misc/batch.js6
-rw-r--r--misc/collapse.js9
-rw-r--r--misc/drupal.js14
-rw-r--r--misc/form.js6
-rw-r--r--misc/tabledrag.js16
-rw-r--r--misc/tableheader.js8
-rw-r--r--misc/tableselect.js3
-rw-r--r--misc/textarea.js18
-rw-r--r--misc/timezone.js2
-rw-r--r--misc/vertical-tabs.js4
-rw-r--r--modules/block/block.js3
-rw-r--r--modules/color/color.js8
-rw-r--r--modules/comment/comment.js4
-rw-r--r--modules/system/system.js32
-rw-r--r--modules/system/system.module10
-rw-r--r--modules/toolbar/toolbar.js14
-rw-r--r--modules/user/user.js4
19 files changed, 73 insertions, 94 deletions
diff --git a/includes/common.inc b/includes/common.inc
index b5686cab7..ce21e4fa4 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -2960,8 +2960,9 @@ function drupal_add_js($data = NULL, $options = NULL) {
'preprocess' => TRUE,
),
);
- // jQuery itself is registered as a library.
+ // Register all required libraries.
drupal_add_library('system', 'jquery');
+ drupal_add_library('system', 'once');
}
switch ($options['type']) {
diff --git a/misc/autocomplete.js b/misc/autocomplete.js
index 87e724d46..489e2fb1b 100644
--- a/misc/autocomplete.js
+++ b/misc/autocomplete.js
@@ -7,7 +7,7 @@
Drupal.behaviors.autocomplete = {
attach: function (context, settings) {
var acdb = [];
- $('input.autocomplete:not(.autocomplete-processed)', context).each(function () {
+ $('input.autocomplete', context).once('autocomplete', function () {
var uri = this.value;
if (!acdb[uri]) {
acdb[uri] = new Drupal.ACDB(uri);
@@ -16,7 +16,6 @@ Drupal.behaviors.autocomplete = {
.attr('autocomplete', 'OFF')[0];
$(input.form).submit(Drupal.autocompleteSubmit);
new Drupal.jsAC(input, acdb[uri]);
- $(this).addClass('autocomplete-processed');
});
}
};
diff --git a/misc/batch.js b/misc/batch.js
index 8e53c210a..d0a32f2a1 100644
--- a/misc/batch.js
+++ b/misc/batch.js
@@ -6,11 +6,7 @@
*/
Drupal.behaviors.batch = {
attach: function (context, settings) {
- // This behavior attaches by ID, so is only valid once on a page.
- if ($('#progress.batch-processed').size()) {
- return;
- }
- $('#progress', context).addClass('batch-processed').each(function () {
+ $('#progress', context).once('batch', function () {
var holder = $(this);
// Success: redirect to the summary.
diff --git a/misc/collapse.js b/misc/collapse.js
index c4fac9be3..741f4b0ba 100644
--- a/misc/collapse.js
+++ b/misc/collapse.js
@@ -53,7 +53,7 @@ Drupal.collapseScrollIntoView = function (node) {
Drupal.behaviors.collapse = {
attach: function (context, settings) {
- $('fieldset.collapsible > legend:not(.collapse-processed)', context).each(function () {
+ $('fieldset.collapsible > legend', context).once('collapse', function () {
var fieldset = $(this.parentNode);
// Expand if there are errors inside
if ($('input.error, textarea.error, select.error', fieldset).size() > 0) {
@@ -81,9 +81,10 @@ Drupal.behaviors.collapse = {
return false;
}))
.append(summary)
- .after($('<div class="fieldset-wrapper"></div>')
- .append(fieldset.children(':not(legend):not(.action)'))
- ).addClass('collapse-processed');
+ .after(
+ $('<div class="fieldset-wrapper"></div>')
+ .append(fieldset.children(':not(legend):not(.action)'))
+ );
});
}
};
diff --git a/misc/drupal.js b/misc/drupal.js
index dbd057bb9..ea688a499 100644
--- a/misc/drupal.js
+++ b/misc/drupal.js
@@ -12,7 +12,6 @@ if ($ === undefined) {
};
}
-
(function ($) {
/**
@@ -38,10 +37,15 @@ if ($ === undefined) {
* loaded, feeding in an element to be processed, in order to attach all
* behaviors to the new content.
*
- * Behaviors should use a class in the form behaviorName-processed to ensure
- * the behavior is attached only once to a given element. (Doing so enables
- * the reprocessing of given elements, which may be needed on occasion despite
- * the ability to limit behavior attachment to a particular element.)
+ * Behaviors should use
+ * @code
+ * $(selector).once('behavior-name', function () {
+ * ...
+ * });
+ * @endcode
+ * to ensure the behavior is attached only once to a given element. (Doing so
+ * enables the reprocessing of given elements, which may be needed on occasion
+ * despite the ability to limit behavior attachment to a particular element.)
*
* @param context
* An element to attach behaviors to. If none is given, the document element
diff --git a/misc/form.js b/misc/form.js
index a59451760..ca572cf38 100644
--- a/misc/form.js
+++ b/misc/form.js
@@ -61,8 +61,7 @@ Drupal.behaviors.formUpdated = {
Drupal.behaviors.multiselectSelector = {
attach: function (context, settings) {
// Automatically selects the right radio button in a multiselect control.
- $('.multiselect select:not(.multiselectSelector-processed)', context)
- .addClass('multiselectSelector-processed').change(function () {
+ $('.multiselect select', context).once('multiselect').change(function () {
$('.multiselect input:radio[value="' + this.id.substr(5) + '"]')
.attr('checked', true);
});
@@ -75,8 +74,7 @@ Drupal.behaviors.multiselectSelector = {
*/
Drupal.behaviors.filterGuidelines = {
attach: function (context) {
- $('.filter-guidelines:not(.filter-guidelines-processed)', context)
- .addClass('filter-guidelines-processed')
+ $('.filter-guidelines', context).once('filter-guidelines')
.find('label').hide()
.parents('.filter-wrapper').find('select.filter-list')
.bind('change', function () {
diff --git a/misc/tabledrag.js b/misc/tabledrag.js
index db40f2437..0c7acbc75 100644
--- a/misc/tabledrag.js
+++ b/misc/tabledrag.js
@@ -15,17 +15,11 @@
Drupal.behaviors.tableDrag = {
attach: function (context, settings) {
for (var base in settings.tableDrag) {
- if (!$('#' + base + '.tabledrag-processed', context).size()) {
- var tableSettings = settings.tableDrag[base];
-
- $('#' + base).filter(':not(.tabledrag-processed)').each(function () {
- // Create the new tableDrag instance. Save in the Drupal variable
- // to allow other scripts access to the object.
- Drupal.tableDrag[base] = new Drupal.tableDrag(this, tableSettings);
- });
-
- $('#' + base).addClass('tabledrag-processed');
- }
+ $('#' + base, context).once('tabledrag', function () {
+ // Create the new tableDrag instance. Save in the Drupal variable
+ // to allow other scripts access to the object.
+ Drupal.tableDrag[base] = new Drupal.tableDrag(this, settings.tableDrag[base]);
+ });
}
}
};
diff --git a/misc/tableheader.js b/misc/tableheader.js
index a49dd2d40..52f6b6d33 100644
--- a/misc/tableheader.js
+++ b/misc/tableheader.js
@@ -17,7 +17,7 @@ Drupal.behaviors.tableHeader = {
// Keep track of all cloned table headers.
var headers = [];
- $('table.sticky-enabled thead:not(.tableHeader-processed)', context).each(function () {
+ $('table.sticky-enabled thead', context).once('tableheader', function () {
// Clone thead so it inherits original jQuery properties.
var headerClone = $(this).clone(true).insertBefore(this.parentNode).wrap('<table class="sticky-header"></table>').parent().css({
position: 'fixed',
@@ -34,7 +34,6 @@ Drupal.behaviors.tableHeader = {
tracker(headerClone);
$(table).addClass('sticky-table');
- $(this).addClass('tableHeader-processed');
});
// Define the anchor holding var.
@@ -81,11 +80,10 @@ Drupal.behaviors.tableHeader = {
// Only attach to scrollbars once, even if Drupal.attachBehaviors is called
// multiple times.
- if (!$('body').hasClass('tableHeader-processed')) {
- $('body').addClass('tableHeader-processed');
+ $('body').once(function () {
$(window).scroll(Drupal.tableHeaderDoScroll);
$(document.documentElement).scroll(Drupal.tableHeaderDoScroll);
- }
+ });
// Track scrolling.
Drupal.tableHeaderOnScroll = function () {
diff --git a/misc/tableselect.js b/misc/tableselect.js
index 332a1d9e5..cba0c57a5 100644
--- a/misc/tableselect.js
+++ b/misc/tableselect.js
@@ -3,7 +3,7 @@
Drupal.behaviors.tableSelect = {
attach: function (context, settings) {
- $('form table:has(th.select-all):not(.tableSelect-processed)', context).each(Drupal.tableSelect);
+ $('form table:has(th.select-all)', context).once('table-select', Drupal.tableSelect);
}
};
@@ -56,7 +56,6 @@ Drupal.tableSelect = function () {
// Keep track of the last checked checkbox.
lastChecked = e.target;
});
- $(this).addClass('tableSelect-processed');
};
Drupal.tableSelectRange = function (from, to, state) {
diff --git a/misc/textarea.js b/misc/textarea.js
index cec9abfff..5938b9e4d 100644
--- a/misc/textarea.js
+++ b/misc/textarea.js
@@ -3,20 +3,16 @@
Drupal.behaviors.textarea = {
attach: function (context, settings) {
- $('textarea.resizable:not(.textarea-processed)', context).each(function () {
- // Avoid non-processed teasers.
- if ($(this).is(('textarea.teaser:not(.teaser-processed)'))) {
- return false;
- }
- var textarea = $(this).addClass('textarea-processed'), staticOffset = null;
-
+ $('textarea.resizable', context).once('textarea', function () {
// When wrapping the text area, work around an IE margin bug. See:
// http://jaspan.com/ie-inherited-margin-bug-form-elements-and-haslayout
- $(this).wrap('<div class="resizable-textarea"><span></span></div>')
- .parent().append($('<div class="grippie"></div>').mousedown(startDrag));
+ var staticOffset = null;
+ var textarea = $(this).wrap('<div class="resizable-textarea"><span></span></div>');
+ var grippie = $('<div class="grippie"></div>').mousedown(startDrag);
- var grippie = $('div.grippie', $(this).parent())[0];
- grippie.style.marginRight = (grippie.offsetWidth - $(this)[0].offsetWidth) + 'px';
+ grippie
+ .insertAfter(textarea)
+ .css('margin-right', grippie.width() - textarea.width());
function startDrag(e) {
staticOffset = textarea.height() - e.pageY;
diff --git a/misc/timezone.js b/misc/timezone.js
index a1e689c92..b708875f5 100644
--- a/misc/timezone.js
+++ b/misc/timezone.js
@@ -6,7 +6,7 @@
*/
Drupal.behaviors.setTimezone = {
attach: function (context, settings) {
- $('select.timezone-detect:not(.timezone-processed)', context).addClass('timezone-processed').each(function () {
+ $('select.timezone-detect', context).once('timezone', function () {
var dateString = Date();
// In some client environments, date strings include a time zone
// abbreviation, between 3 and 5 letters enclosed in parentheses,
diff --git a/misc/vertical-tabs.js b/misc/vertical-tabs.js
index 7fb15549f..32c4ee697 100644
--- a/misc/vertical-tabs.js
+++ b/misc/vertical-tabs.js
@@ -15,7 +15,7 @@
*/
Drupal.behaviors.verticalTabs = {
attach: function (context) {
- $('.vertical-tabs-panes:not(.vertical-tabs-processed)', context).each(function () {
+ $('.vertical-tabs-panes', context).once('vertical-tabs', function () {
var focusID = $(':hidden.vertical-tabs-active-tab', this).val();
var focus;
// Create the tab column.
@@ -42,7 +42,7 @@ Drupal.behaviors.verticalTabs = {
focus = $('> .vertical-tabs-pane:first', this);
}
focus.data('verticalTab').focus();
- }).addClass('vertical-tabs-processed');
+ });
}
};
diff --git a/modules/block/block.js b/modules/block/block.js
index 34c04d246..e83f6ea04 100644
--- a/modules/block/block.js
+++ b/modules/block/block.js
@@ -51,7 +51,7 @@ Drupal.behaviors.blockDrag = {
};
// Add the behavior to each region select list.
- $('select.block-region-select:not(.blockregionselect-processed)', context).each(function () {
+ $('select.block-region-select', context).once('block-region-select', function () {
$(this).change(function (event) {
// Make our new row and select field.
var row = $(this).parents('tr:first');
@@ -82,7 +82,6 @@ Drupal.behaviors.blockDrag = {
// Remove focus from selectbox.
select.get(0).blur();
});
- $(this).addClass('blockregionselect-processed');
});
var checkEmptyRegions = function (table, rowObject) {
diff --git a/modules/color/color.js b/modules/color/color.js
index c286c40a9..a834f11ed 100644
--- a/modules/color/color.js
+++ b/modules/color/color.js
@@ -4,10 +4,10 @@
Drupal.behaviors.color = {
attach: function (context, settings) {
// This behavior attaches by ID, so is only valid once on a page.
- if ($('#color_scheme_form .color-form.color-processed').size()) {
+ var form = $('#system-theme-settings .color-form', context).once('color');
+ if (form.length == 0) {
return;
}
- var form = $('#system-theme-settings .color-form', context);
var inputs = [];
var hooks = [];
var locks = [];
@@ -24,9 +24,7 @@ Drupal.behaviors.color = {
}
// Build a preview.
- $('#preview:not(.color-processed)')
- .append('<div id="gradient"></div>')
- .addClass('color-processed');
+ $('#preview').once('color').append('<div id="gradient"></div>');
var gradient = $('#preview #gradient');
var h = parseInt(gradient.css('height')) / 10;
for (i = 0; i < h; ++i) {
diff --git a/modules/comment/comment.js b/modules/comment/comment.js
index bdd1c3b95..58f00dc6b 100644
--- a/modules/comment/comment.js
+++ b/modules/comment/comment.js
@@ -6,9 +6,7 @@ Drupal.behaviors.comment = {
$.each(['name', 'homepage', 'mail'], function () {
var cookie = Drupal.comment.getCookie('comment_info_' + this);
if (cookie) {
- $('#comment-form input[name=' + this + ']:not(.comment-processed)', context)
- .val(cookie)
- .addClass('comment-processed');
+ $('#comment-form input[name=' + this + ']', context).once('comment').val(cookie);
}
});
}
diff --git a/modules/system/system.js b/modules/system/system.js
index 11aa8b4b8..4da80811d 100644
--- a/modules/system/system.js
+++ b/modules/system/system.js
@@ -32,7 +32,7 @@ Drupal.behaviors.cleanURLsSettingsCheck = {
// This behavior attaches by ID, so is only valid once on a page.
// Also skip if we are on an install page, as Drupal.cleanURLsInstallCheck will handle
// the processing.
- if (!($('#edit-clean-url').size()) || $('.clean-url-processed, #edit-clean-url.install').size()) {
+ if (!($('#edit-clean-url').length) || $('#edit-clean-url.install').once('clean-url').length) {
return;
}
var url = settings.basePath + 'admin/config/search/clean-urls/check';
@@ -44,7 +44,6 @@ Drupal.behaviors.cleanURLsSettingsCheck = {
location = settings.basePath +"admin/config/search/clean-urls";
}
});
- $('#clean-url').addClass('clean-url-processed');
}
};
@@ -68,7 +67,6 @@ Drupal.cleanURLsInstallCheck = function () {
$('#edit-clean-url').attr('value', 1);
}
});
- $('#edit-clean-url').addClass('clean-url-processed');
};
/**
@@ -79,21 +77,17 @@ Drupal.cleanURLsInstallCheck = function () {
Drupal.behaviors.copyFieldValue = {
attach: function (context, settings) {
for (var sourceId in settings.copyFieldValue) {
- // Get the list of target fields.
- targetIds = settings.copyFieldValue[sourceId];
- if (!$('#'+ sourceId + '.copy-field-values-processed', context).size()) {
+ $('#' + sourceId, context).once('copy-field-values').bind('blur', function () {
+ // Get the list of target fields.
+ var targetIds = settings.copyFieldValue[sourceId];
// Add the behavior to update target fields on blur of the primary field.
- sourceField = $('#' + sourceId);
- sourceField.bind('blur', function () {
- for (var delta in targetIds) {
- var targetField = $('#'+ targetIds[delta]);
- if (targetField.val() == '') {
- targetField.val(this.value);
- }
+ for (var delta in targetIds) {
+ var targetField = $('#' + targetIds[delta]);
+ if (targetField.val() == '') {
+ targetField.val(this.value);
}
- });
- sourceField.addClass('copy-field-values-processed');
- }
+ }
+ });
}
}
};
@@ -104,12 +98,12 @@ Drupal.behaviors.copyFieldValue = {
Drupal.behaviors.dateTime = {
attach: function (context, settings) {
// Show/hide custom format depending on the select's value.
- $('select.date-format:not(.date-time-processed)', context).change(function () {
- $(this).addClass('date-time-processed').parents('div.date-container').children('div.custom-container')[$(this).val() == 'custom' ? 'show' : 'hide']();
+ $('select.date-format', context).once('date-time').change(function () {
+ $(this).parents('div.date-container').children('div.custom-container')[$(this).val() == 'custom' ? 'show' : 'hide']();
});
// Attach keyup handler to custom format inputs.
- $('input.custom-format:not(.date-time-processed)', context).addClass('date-time-processed').keyup(function () {
+ $('input.custom-format', context).once('date-time').keyup(function () {
var input = $(this);
var url = settings.dateTime.lookup + (settings.dateTime.lookup.match(/\?q=/) ? '&format=' : '?format=') + encodeURIComponent(input.val());
$.getJSON(url, function (data) {
diff --git a/modules/system/system.module b/modules/system/system.module
index b975c8597..d9a29a0cb 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -933,6 +933,16 @@ function system_library() {
),
);
+ // jQuery Once.
+ $libraries['once'] = array(
+ 'title' => 'jQuery Once',
+ 'website' => 'http://plugins.jquery.com/project/once',
+ 'version' => '1.2',
+ 'js' => array(
+ 'misc/jquery.once.js' => array('weight' => JS_LIBRARY - 19),
+ ),
+ );
+
// jQuery Form Plugin.
$libraries['form'] = array(
'title' => 'jQuery Form Plugin',
diff --git a/modules/toolbar/toolbar.js b/modules/toolbar/toolbar.js
index 7b804182f..c19010039 100644
--- a/modules/toolbar/toolbar.js
+++ b/modules/toolbar/toolbar.js
@@ -8,18 +8,12 @@ Drupal.behaviors.admin = {
attach: function() {
// Set the intial state of the toolbar.
- $('#toolbar:not(.processed)').each(function() {
- Drupal.admin.toolbar.init();
- $(this).addClass('processed');
- });
+ $('#toolbar', context).once('toolbar', Drupal.admin.toolbar.init);
// Toggling of admin shortcuts visibility.
- $('#toolbar span.toggle:not(.processed)').each(function() {
- $(this).click(function() {
- Drupal.admin.toolbar.toggle();
- return false;
- });
- $(this).addClass('processed');
+ $('#toolbar span.toggle', context).once('toolbar-toggle').click(function() {
+ Drupal.admin.toolbar.toggle();
+ return false;
});
}
};
diff --git a/modules/user/user.js b/modules/user/user.js
index 9d75911fa..d382375ab 100644
--- a/modules/user/user.js
+++ b/modules/user/user.js
@@ -8,8 +8,8 @@
Drupal.behaviors.password = {
attach: function (context, settings) {
var translate = settings.password;
- $('input.password-field:not(.password-processed)', context).each(function () {
- var passwordInput = $(this).addClass('password-processed');
+ $('input.password-field', context).once('password', function () {
+ var passwordInput = $(this);
var innerWrapper = $(this).parent();
var outerWrapper = $(this).parent().parent();