Commit c11cb4ec authored by Dries's avatar Dries

- Patch #120360 by nedjo: enable AJAX by making all behaviours reattachable.

parent a3d75e54
......@@ -61,6 +61,7 @@ Drupal 6.0, xxxx-xx-xx (development version)
- Added support for configurable actions.
- Made user profiles easier to theme by using array rendering and supplying template files.
- Added Deletion API to allow modules to alter and respond to any delete operation.
- Added the Update status module to automatically check for available updates and warn sites if they are missing security updates or newer versions.
Drupal 5.0, 2007-01-15
----------------------
......
......@@ -3,9 +3,9 @@
/**
* Attaches the autocomplete behaviour to all required fields
*/
Drupal.autocompleteAutoAttach = function () {
Drupal.behaviors.autocomplete = function (context) {
var acdb = [];
$('input.autocomplete').each(function () {
$('input.autocomplete:not(.autocomplete-processed)', context).each(function () {
var uri = this.value;
if (!acdb[uri]) {
acdb[uri] = new Drupal.ACDB(uri);
......@@ -14,6 +14,7 @@ Drupal.autocompleteAutoAttach = function () {
.attr('autocomplete', 'OFF')[0];
$(input.form).submit(Drupal.autocompleteSubmit);
new Drupal.jsAC(input, acdb[uri]);
$(this).addClass('autocomplete-processed');
});
};
......@@ -296,8 +297,3 @@ Drupal.ACDB.prototype.cancel = function() {
if (this.timer) clearTimeout(this.timer);
this.searchString = '';
};
// Global Killswitch
if (Drupal.jsEnabled) {
$(document).ready(Drupal.autocompleteAutoAttach);
}
if (Drupal.jsEnabled) {
$(document).ready(function() {
$('#progress').each(function () {
var holder = this;
var uri = Drupal.settings.batch.uri;
var initMessage = Drupal.settings.batch.initMessage;
var errorMessage = Drupal.settings.batch.errorMessage;
// $Id$
// Success: redirect to the summary.
var updateCallback = function (progress, status, pb) {
if (progress == 100) {
pb.stopMonitoring();
window.location = uri+'&op=finished';
}
};
/**
* Attaches the batch behaviour to progress bars.
*/
Drupal.behaviors.batch = function (context) {
// 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 () {
var holder = this;
var uri = Drupal.settings.batch.uri;
var initMessage = Drupal.settings.batch.initMessage;
var errorMessage = Drupal.settings.batch.errorMessage;
var errorCallback = function (pb) {
var div = document.createElement('p');
div.className = 'error';
$(div).html(errorMessage);
$(holder).prepend(div);
$('#wait').hide();
};
// Success: redirect to the summary.
var updateCallback = function (progress, status, pb) {
if (progress == 100) {
pb.stopMonitoring();
window.location = uri+'&op=finished';
}
};
var progress = new Drupal.progressBar('updateprogress', updateCallback, "POST", errorCallback);
progress.setProgress(-1, initMessage);
$(holder).append(progress.element);
progress.startMonitoring(uri+'&op=do', 10);
});
var errorCallback = function (pb) {
var div = document.createElement('p');
div.className = 'error';
$(div).html(errorMessage);
$(holder).prepend(div);
$('#wait').hide();
};
var progress = new Drupal.progressBar('updateprogress', updateCallback, "POST", errorCallback);
progress.setProgress(-1, initMessage);
$(holder).append(progress.element);
progress.startMonitoring(uri+'&op=do', 10);
});
}
};
......@@ -20,10 +20,6 @@ Drupal.toggleFieldset = function(fieldset) {
Drupal.collapseScrollIntoView(this.parentNode);
}
});
if (typeof(Drupal.textareaAttach) != 'undefined') {
// Initialize resizable textareas that are now revealed
Drupal.textareaAttach(null, fieldset);
}
}
else {
var content = $('> div', fieldset).slideUp('medium', function() {
......@@ -50,19 +46,17 @@ Drupal.collapseScrollIntoView = function (node) {
}
};
// Global Killswitch
if (Drupal.jsEnabled) {
$(document).ready(function() {
$('fieldset.collapsible > legend').each(function() {
var fieldset = $(this.parentNode);
// Expand if there are errors inside
if ($('input.error, textarea.error, select.error', fieldset).size() > 0) {
fieldset.removeClass('collapsed');
}
Drupal.behaviors.collapse = function (context) {
$('fieldset.collapsible > legend:not(.collapse-processed)', context).each(function() {
var fieldset = $(this.parentNode);
// Expand if there are errors inside
if ($('input.error, textarea.error, select.error', fieldset).size() > 0) {
fieldset.removeClass('collapsed');
}
// Turn the legend into a clickable link and wrap the contents of the fieldset
// in a div for easier animation
var text = this.innerHTML;
// Turn the legend into a clickable link and wrap the contents of the fieldset
// in a div for easier animation
var text = this.innerHTML;
$(this).empty().append($('<a href="#">'+ text +'</a>').click(function() {
var fieldset = $(this).parents('fieldset:first')[0];
// Don't animate multiple times
......@@ -71,7 +65,9 @@ if (Drupal.jsEnabled) {
Drupal.toggleFieldset(fieldset);
}
return false;
})).after($('<div class="fieldset-wrapper"></div>').append(fieldset.children(':not(legend)')));
});
}))
.after($('<div class="fieldset-wrapper"></div>')
.append(fieldset.children(':not(legend)')))
.addClass('collapse-processed');
});
}
};
// $Id$
var Drupal = Drupal || { 'settings': {}, 'themes': {}, 'locale': {} };
var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'themes': {}, 'locale': {} };
/**
* Set the variable that indicates if JavaScript behaviors should be applied
......@@ -21,6 +21,43 @@ Drupal.extend = function(obj) {
}
};
/**
* Attach all registered behaviors to a page element.
*
* Behaviors are event-triggered actions that attach to page elements, enhancing
* default non-Javascript UIs. Behaviors are registered in the Drupal.behaviors
* object as follows:
* @code
* Drupal.behaviors.behaviorName = function () {
* ...
* };
* @endcode
*
* Drupal.attachBehaviors is added below to the jQuery ready event and so
* runs on initial page load. Developers implementing AHAH/AJAX in their
* solutions should also call this function after new page content has been
* 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.)
*
* @param context
* An element to attach behaviors to. If none is given, the document element
* is used.
*/
Drupal.attachBehaviors = function(context) {
context = context || document;
if (Drupal.jsEnabled) {
// Execute all of them.
jQuery.each(Drupal.behaviors, function() {
this(context);
});
}
};
/**
* Encode special characters in a plain-text string for display as HTML.
*/
......@@ -362,6 +399,8 @@ if (Drupal.jsEnabled) {
document.documentElement.className = 'js';
// 'js enabled' cookie
document.cookie = 'has_js=1';
// Attach all behaviors.
$(document).ready(Drupal.attachBehaviors);
}
/**
......
// $Id$
// Global Killswitch
if (Drupal.jsEnabled) {
Drupal.behaviors.tableHeader = function (context) {
// Keep track of all header cells.
var cells = [];
// Attach to all headers.
$(document).ready(function() {
var z = 0;
$('table thead').each(function () {
// Find table height.
var table = $(this).parent('table')[0];
var height = $(table).addClass('sticky-table').height();
var i = 0;
var z = 0;
$('table thead:not(.tableHeader-processed)', context).each(function () {
// Find table height.
var table = $(this).parent('table')[0];
var height = $(table).addClass('sticky-table').height();
var i = 0;
// Find all header cells.
$('th', this).each(function () {
// Find all header cells.
$('th', this).each(function () {
// Ensure each cell has an element in it.
var html = $(this).html();
if (html == ' ') {
html = '&nbsp;';
}
if ($(this).children().size() == 0) {
html = '<span>'+ html +'</span>';
}
// Ensure each cell has an element in it.
var html = $(this).html();
if (html == ' ') {
html = '&nbsp;';
}
if ($(this).children().size() == 0) {
html = '<span>'+ html +'</span>';
}
// Clone and wrap cell contents in sticky wrapper that overlaps the cell's padding.
$('<div class="sticky-header" style="position: fixed; visibility: hidden; top: 0px;">'+ html +'</div>').prependTo(this);
var div = $('div.sticky-header', this).css({
'marginLeft': '-'+ $(this).css('paddingLeft'),
'marginRight': '-'+ $(this).css('paddingRight'),
'paddingLeft': $(this).css('paddingLeft'),
'paddingTop': $(this).css('paddingTop'),
'paddingBottom': $(this).css('paddingBottom'),
'z-index': ++z
})[0];
cells.push(div);
// Clone and wrap cell contents in sticky wrapper that overlaps the cell's padding.
$('<div class="sticky-header" style="position: fixed; visibility: hidden; top: 0px;">'+ html +'</div>').prependTo(this);
var div = $('div.sticky-header', this).css({
'marginLeft': '-'+ $(this).css('paddingLeft'),
'marginRight': '-'+ $(this).css('paddingRight'),
'paddingLeft': $(this).css('paddingLeft'),
'paddingTop': $(this).css('paddingTop'),
'paddingBottom': $(this).css('paddingBottom'),
'z-index': ++z
})[0];
cells.push(div);
// Adjust width to fit cell/table.
var ref = this;
if (!i++) {
// The first cell is as wide as the table to prevent gaps.
ref = table;
div.wide = true;
}
$(div).css('width', parseInt($(ref).width())
- parseInt($(div).css('paddingLeft')) +'px');
// Adjust width to fit cell/table.
var ref = this;
if (!i++) {
// The first cell is as wide as the table to prevent gaps.
ref = table;
div.wide = true;
}
$(div).css('width', parseInt($(ref).width())
- parseInt($(div).css('paddingLeft')) +'px');
// Get position and store.
div.cell = this;
div.table = table;
div.stickyMax = height;
div.stickyPosition = Drupal.absolutePosition(this).y;
});
// Get position and store.
div.cell = this;
div.table = table;
div.stickyMax = height;
div.stickyPosition = Drupal.absolutePosition(this).y;
});
$(this).addClass('tableHeader-processed');
});
// Track scrolling.
......@@ -108,4 +105,4 @@ if (Drupal.jsEnabled) {
}, 250);
};
$(window).resize(resize);
}
};
// $Id$
Drupal.behaviors.tableSelect = function (context) {
$('form table[th.select-all]:not(.tableSelect-processed)', context).each(Drupal.tableSelect);
}
Drupal.tableSelect = function() {
// Keep track of the table, which checkbox is checked and alias the settings.
var table = this, selectAll, checkboxes, lastChecked;
......@@ -41,6 +45,7 @@ Drupal.tableSelect = function() {
// Keep track of the last checked checkbox.
lastChecked = e.target;
});
$(this).addClass('tableSelect-processed');
};
Drupal.tableSelectRange = function(from, to, state) {
......@@ -67,10 +72,3 @@ Drupal.tableSelectRange = function(from, to, state) {
}
};
// Global Killswitch
if (Drupal.jsEnabled) {
$(document).ready(function() {
$('form table[th.select-all]').each(Drupal.tableSelect);
});
}
......@@ -5,9 +5,9 @@
*
* Note: depends on resizable textareas.
*/
Drupal.teaserAttach = function() {
$('textarea.teaser:not(.joined)').each(function() {
var teaser = $(this).addClass('joined');
Drupal.behaviors.teaser = function(context) {
$('textarea.teaser:not(.teaser-processed)', context).each(function() {
var teaser = $(this).addClass('teaser-processed');
// Move teaser textarea before body, and remove its form-item wrapper.
var body = $('#'+ Drupal.settings.teaser[this.id]);
......@@ -75,7 +75,3 @@ Drupal.teaserAttach = function() {
});
};
if (Drupal.jsEnabled) {
$(document).ready(Drupal.teaserAttach);
}
// $Id$
Drupal.textareaAttach = function() {
$('textarea.resizable:not(.processed)').each(function() {
var textarea = $(this).addClass('processed'), staticOffset = null;
Drupal.behaviors.textarea = function(context) {
$('textarea.resizable:not(.textarea-processed)', context).each(function() {
var textarea = $(this).addClass('textarea-processed'), staticOffset = null;
// When wrapping the text area, work around an IE margin bug. See:
// http://jaspan.com/ie-inherited-margin-bug-form-elements-and-haslayout
......@@ -37,7 +37,3 @@ Drupal.textareaAttach = function() {
}
});
};
if (Drupal.jsEnabled) {
$(document).ready(Drupal.textareaAttach);
}
......@@ -3,8 +3,8 @@
/**
* Attaches the upload behaviour to the upload form.
*/
Drupal.uploadAutoAttach = function() {
$('input.upload').each(function () {
Drupal.behaviors.upload = function(context) {
$('input.upload:not(.upload-processed)', context).addClass('upload-processed').each(function () {
var uri = this.value;
// Extract the base name from the id (edit-attach-url -> attach).
var base = this.id.substring(5, this.id.length - 4);
......@@ -12,6 +12,7 @@ Drupal.uploadAutoAttach = function() {
var wrapper = base + '-wrapper';
var hide = base + '-hide';
var upload = new Drupal.jsUpload(uri, button, wrapper, hide);
$(this).addClass('upload-processed');
});
};
......@@ -65,11 +66,10 @@ Drupal.jsUpload.prototype.oncomplete = function (data) {
// If uploading the first attachment fade in everything
if ($('tr', div).size() == 2) {
// Replace form and re-attach behaviour
// Replace form and re-attach behaviours
$(div).hide();
$(this.wrapper).append(div);
$(div).fadeIn('slow');
Drupal.uploadAutoAttach();
}
// Else fade in only the last table row
else {
......@@ -89,8 +89,8 @@ Drupal.jsUpload.prototype.oncomplete = function (data) {
$(this.wrapper).append(div);
$('table tr:last-of-type td', div).fadeIn('slow');
$(this.hide, div).fadeIn('slow');
Drupal.uploadAutoAttach();
}
Drupal.attachBehaviors(div);
Drupal.unfreezeHeight();
};
......@@ -108,9 +108,3 @@ Drupal.jsUpload.prototype.onerror = function (error) {
left: '0px'
});
};
// Global killswitch
if (Drupal.jsEnabled) {
$(document).ready(Drupal.uploadAutoAttach);
}
......@@ -7,7 +7,7 @@ function actions_schema() {
'aid' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '0'),
'type' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''),
'callback' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
'parameters' => array('type' => 'text', 'not null' => TRUE, 'size' => 'big', 'default' => ''),
'parameters' => array('type' => 'text', 'not null' => TRUE, 'size' => 'big'),
'description' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '0'),
),
'primary key' => array('aid'),
......
// $Id$
if (Drupal.jsEnabled) {
$(document).ready(function () {
var form = $('#color_scheme_form .color-form');
var inputs = [];
var hooks = [];
var locks = [];
var focused = null;
// Add Farbtastic
$(form).prepend('<div id="placeholder"></div>');
var farb = $.farbtastic('#placeholder');
// Decode reference colors to HSL
var reference = Drupal.settings.color.reference;
for (i in reference) {
reference[i] = farb.RGBToHSL(farb.unpack(reference[i]));
}
// Build preview
$('#preview').append('<div id="gradient"></div>');
var gradient = $('#preview #gradient');
var h = parseInt(gradient.css('height')) / 10;
for (i = 0; i < h; ++i) {
gradient.append('<div class="gradient-line"></div>');
}
// Fix preview background in IE6
if (navigator.appVersion.match(/MSIE [0-6]\./)) {
var e = $('#preview #img')[0];
var image = e.currentStyle.backgroundImage;
e.style.backgroundImage = 'none';
e.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image.substring(5, image.length - 2) + "')";
Drupal.behaviors.color = function (context) {
// This behavior attaches by ID, so is only valid once on a page.
if ($('#color_scheme_form .color-form.color-processed').size()) {
return;
}
var form = $('#color_scheme_form .color-form', context);
var inputs = [];
var hooks = [];
var locks = [];
var focused = null;
// Add Farbtastic
$(form).prepend('<div id="placeholder"></div>').addClass('color-processed');
var farb = $.farbtastic('#placeholder');
// Decode reference colors to HSL
var reference = Drupal.settings.color.reference;
for (i in reference) {
reference[i] = farb.RGBToHSL(farb.unpack(reference[i]));
}
// Build preview
$('#preview:not(.color-processed)')
.append('<div id="gradient"></div>')
.addClass('color-processed');
var gradient = $('#preview #gradient');
var h = parseInt(gradient.css('height')) / 10;
for (i = 0; i < h; ++i) {
gradient.append('<div class="gradient-line"></div>');
}
// Fix preview background in IE6
if (navigator.appVersion.match(/MSIE [0-6]\./)) {
var e = $('#preview #img')[0];
var image = e.currentStyle.backgroundImage;
e.style.backgroundImage = 'none';
e.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image.substring(5, image.length - 2) + "')";
}
// Set up colorscheme selector
$('#edit-scheme', form).change(function () {
var colors = this.options[this.selectedIndex].value;
if (colors != '') {
colors = colors.split(',');
for (i in colors) {
callback(inputs[i], colors[i], false, true);
}
preview();
}
});
// Set up colorscheme selector
$('#edit-scheme', form).change(function () {
var colors = this.options[this.selectedIndex].value;
if (colors != '') {
colors = colors.split(',');
for (i in colors) {
callback(inputs[i], colors[i], false, true);
}
preview();
/**
* Render the preview.
*/
function preview() {
// Solid background
$('#preview', form).css('backgroundColor', inputs[0].value);
// Text preview
$('#text', form).css('color', inputs[4].value);
$('#text a, #text h2', form).css('color', inputs[1].value);
// Set up gradient
var top = farb.unpack(inputs[2].value);
var bottom = farb.unpack(inputs[3].value);
if (top && bottom) {
var delta = [];
for (i in top) {
delta[i] = (bottom[i] - top[i]) / h;
}
});
var accum = top;
/**
* Render the preview.
*/
function preview() {
// Solid background
$('#preview', form).css('backgroundColor', inputs[0].value);
// Text preview
$('#text', form).css('color', inputs[4].value);
$('#text a, #text h2', form).css('color', inputs[1].value);
// Set up gradient
var top = farb.unpack(inputs[2].value);
var bottom = farb.unpack(inputs[3].value);
if (top && bottom) {
var delta = [];
for (i in top) {
delta[i] = (bottom[i] - top[i]) / h;
// Render gradient lines
$('#gradient > div', form).each(function () {
for (i in accum) {
accum[i] += delta[i];
}
var accum = top;
// Render gradient lines
$('#gradient > div', form).each(function () {
for (i in accum) {
accum[i] += delta[i];
}
this.style.backgroundColor = farb.pack(accum);
});
}
this.style.backgroundColor = farb.pack(accum);
});
}
/**
* Shift a given color, using a reference pair (ref in HSL).
*
* This algorithm ensures relative ordering on the saturation and luminance
* axes is preserved, and performs a simple hue shift.
*
* It is also symmetrical. If: shift_color(c, a, b) == d,
* then shift_color(d, b, a) == c.
*/
function shift_color(given, ref1, ref2) {
// Convert to HSL
given = farb.RGBToHSL(farb.unpack(given));
// Hue: apply delta
given[0] += ref2[0] - ref1[0];
// Saturation: interpolate
if (ref1[1] == 0 || ref2[1] == 0) {
given[1] = ref2[1];
}
/**
* Shift a given color, using a reference pair (ref in HSL).
*
* This algorithm ensures relative ordering on the saturation and luminance
* axes is preserved, and performs a simple hue shift.
*
* It is also symmetrical. If: shift_color(c, a, b) == d,
* then shift_color(d, b, a) == c.
*/
function shift_color(given, ref1, ref2) {
// Convert to HSL
given = farb.RGBToHSL(farb.unpack(given));
// Hue: apply delta
given[0] += ref2[0] - ref1[0];
// Saturation: interpolate
if (ref1[1] == 0 || ref2[1] == 0) {
given[1] = ref2[1];
}
else {
var d = ref1[1] / ref2[1];
if (d > 1) {
given[1] /= d;
}
else {
var d = ref1[1] / ref2[1];
if (d > 1) {
given[1] /= d;
}
else {
given[1] = 1 - (1 - given[1]) * d;
}
given[1] = 1 - (1 - given[1]) * d;
}
}
// Luminance: interpolate
if (ref1[2] == 0 || ref2[2] == 0) {
given[2] = ref2[2];
// Luminance: interpolate
if (ref1[2] == 0 || ref2[2] == 0) {
given[2] = ref2[2];
}
else {
var d = ref1[2] / ref2[2];
if (d > 1) {
given[2] /= d;
}
else {
var d = ref1[2] / ref2[2];
if (d > 1) {
given[2] /= d;
}
else {
given[2] = 1 - (1 - given[2]) * d;
}
given[2] = 1 - (1 - given[2]) * d;
}
return farb.pack(farb.HSLToRGB(given));
}
/**
* Callback for Farbtastic when a new color is chosen.
*/
function callback(input, color, propagate, colorscheme) {
// Set background/foreground color
$(input).css({
backgroundColor: color,
'color': farb.RGBToHSL(farb.unpack(color))[2] > 0.5 ? '#000' : '#fff'
});
return farb.pack(farb.HSLToRGB(given));
}
/**
* Callback for Farbtastic when a new color is chosen.
*/
function callback(input, color, propagate, colorscheme) {
// Set background/foreground color
$(input).css({
backgroundColor: color,
'color': farb.RGBToHSL(farb.unpack(color))[2] > 0.5 ? '#000' : '#fff'
});