/**
* jQuery.fn.sortElements
* --------------
* @param Function comparator:
* Exactly the same behaviour as [1,2,3].sort(comparator)
*
* @param Function getSortable
* A function that should return the element that is
* to be sorted. The comparator will run on the
* current collection, but you may want the actual
* resulting sort to occur on a parent or another
* associated element.
*
* E.g. $('td').sortElements(comparator, function(){
* return this.parentNode;
* })
*
* The
) will be sorted instead
* of the itself.
*
* Credit: http://james.padolsey.com/javascript/sorting-elements-with-jquery/
*
*/
jQuery.fn.sortElements = (function(){
var sort = [].sort;
return function(comparator, getSortable) {
getSortable = getSortable || function(){return this;};
var placements = this.map(function(){
var sortElement = getSortable.call(this),
parentNode = sortElement.parentNode,
// Since the element itself will change position, we have
// to have some way of storing its original position in
// the DOM. The easiest way is to have a 'flag' node:
nextSibling = parentNode.insertBefore(
document.createTextNode(''),
sortElement.nextSibling
);
return function() {
if (parentNode === this) {
throw new Error(
"You can't sort elements if any one is a descendant of another."
);
}
// Insert before flag:
parentNode.insertBefore(this, nextSibling);
// Remove flag:
parentNode.removeChild(nextSibling);
};
});
return sort.call(this, comparator).each(function(i){
placements[i].call(getSortable.call(this));
});
};
})();
(function ($) {
Drupal.behaviors.features = {
attach: function(context, settings) {
// Features management form
$('table.features:not(.processed)', context).each(function() {
$(this).addClass('processed');
// Check the overridden status of each feature
Drupal.features.checkStatus();
// Add some nicer row hilighting when checkboxes change values
$('input', this).bind('change', function() {
if (!$(this).attr('checked')) {
$(this).parents('tr').removeClass('enabled').addClass('disabled');
}
else {
$(this).parents('tr').addClass('enabled').removeClass('disabled');
}
});
});
// Export form component selector
$('form.features-export-form select.features-select-components:not(.processed)', context).each(function() {
$(this)
.addClass('processed')
.change(function() {
var target = $(this).val();
$('div.features-select').hide();
$('div.features-select-' + target).show();
return false;
}).trigger('change');
});
//View info dialog
var infoDialog = $('#features-info-file');
if (infoDialog.length != 0) {
infoDialog.dialog({
autoOpen: false,
modal: true,
draggable: false,
resizable: false,
width: 600,
height: 480
});
}
if ((Drupal.settings.features != undefined) && (Drupal.settings.features.info != undefined)) {
$('#features-info-file textarea').val(Drupal.settings.features.info);
$('#features-info-file').dialog('open');
//To be reset by the button click ajax
Drupal.settings.features.info = undefined;
}
// mark any conflicts with a class
if ((Drupal.settings.features != undefined) && (Drupal.settings.features.conflicts != undefined)) {
for (var moduleName in Drupal.settings.features.conflicts) {
moduleConflicts = Drupal.settings.features.conflicts[moduleName];
$('#features-export-wrapper input[type=checkbox]', context).each(function() {
if (!$(this).hasClass('features-checkall')) {
var key = $(this).attr('name');
var matches = key.match(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/);
if (matches != null) {
var component = matches[1];
var item = matches[4];
if ((component in moduleConflicts) && (moduleConflicts[component].indexOf(item) != -1)) {
$(this).parent().addClass('features-conflict');
}
}
}
});
}
}
function _checkAll(value) {
if (value) {
$('#features-export-wrapper .component-select input[type=checkbox]:visible', context).each(function() {
var move_id = $(this).attr('id');
$(this).click();
$('#'+ move_id).attr('checked', 'checked');
});
}
else {
$('#features-export-wrapper .component-added input[type=checkbox]:visible', context).each(function() {
var move_id = $(this).attr('id');
$('#'+ move_id).removeAttr('checked');
$(this).click();
$('#'+ move_id).removeAttr('checked');
});
}
}
function updateComponentCountInfo(item, section) {
switch (section) {
case 'select':
var parent = $(item).closest('.features-export-list').siblings('.features-export-component');
$('.component-count', parent).text(function (index, text) {
return +text + 1;
}
);
break;
case 'added':
case 'detected':
var parent = $(item).closest('.features-export-component');
$('.component-count', parent).text(function (index, text) {
return text - 1;
});
}
}
function moveCheckbox(item, section, value) {
updateComponentCountInfo(item, section);
var curParent = item;
if ($(item).hasClass('form-type-checkbox')) {
item = $(item).children('input[type=checkbox]');
}
else {
curParent = $(item).parents('.form-type-checkbox');
}
var newParent = $(curParent).parents('.features-export-parent').find('.form-checkboxes.component-'+section);
$(curParent).detach();
$(curParent).appendTo(newParent);
var list = ['select', 'added', 'detected', 'included'];
for (i in list) {
$(curParent).removeClass('component-' + list[i]);
$(item).removeClass('component-' + list[i]);
}
$(curParent).addClass('component-'+section);
$(item).addClass('component-'+section);
if (value) {
$(item).attr('checked', 'checked');
}
else {
$(item).removeAttr('checked')
}
$(newParent).parent().removeClass('features-export-empty');
// re-sort new list of checkboxes based on labels
$(newParent).find('label').sortElements(
function(a, b){
return $(a).text() > $(b).text() ? 1 : -1;
},
function(){
return this.parentNode;
}
);
}
// provide timer for auto-refresh trigger
var timeoutID = 0;
var inTimeout = 0;
function _triggerTimeout() {
timeoutID = 0;
_updateDetected();
}
function _resetTimeout() {
inTimeout++;
// if timeout is already active, reset it
if (timeoutID != 0) {
window.clearTimeout(timeoutID);
if (inTimeout > 0) inTimeout--;
}
timeoutID = window.setTimeout(_triggerTimeout, 500);
}
function _updateDetected() {
var autodetect = $('#features-autodetect input[type=checkbox]');
if ((autodetect.length > 0) && (!autodetect.is(':checked'))) return;
// query the server for a list of components/items in the feature and update
// the auto-detected items
var items = []; // will contain a list of selected items exported to feature
var components = {}; // contains object of component names that have checked items
$('#features-export-wrapper input[type=checkbox]:checked', context).each(function() {
if (!$(this).hasClass('features-checkall')) {
var key = $(this).attr('name');
var matches = key.match(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/);
components[matches[1]] = matches[1];
if (!$(this).hasClass('component-detected')) {
items.push(key);
}
}
});
var featureName = $('#edit-module-name').val();
if (featureName == '') {
featureName = '*';
}
var url = Drupal.settings.basePath + 'features/ajaxcallback/' + featureName;
var excluded = Drupal.settings.features.excluded;
var postData = {'items': items, 'excluded': excluded};
jQuery.post(url, postData, function(data) {
if (inTimeout > 0) inTimeout--;
// if we have triggered another timeout then don't update with old results
if (inTimeout == 0) {
// data is an object keyed by component listing the exports of the feature
for (var component in data) {
var itemList = data[component];
$('#features-export-wrapper .component-' + component + ' input[type=checkbox]', context).each(function() {
var key = $(this).attr('value');
// first remove any auto-detected items that are no longer in component
if ($(this).hasClass('component-detected')) {
if (!(key in itemList)) {
moveCheckbox(this, 'select', false)
}
}
// next, add any new auto-detected items
else if ($(this).hasClass('component-select')) {
if (key in itemList) {
moveCheckbox(this, 'detected', itemList[key]);
$(this).parent().show(); // make sure it's not hidden from filter
}
}
});
}
// loop over all selected components and check for any that have been completely removed
for (var component in components) {
if ((data == null) || !(component in data)) {
$('#features-export-wrapper .component-' + component + ' input[type=checkbox].component-detected', context).each(function() {
moveCheckbox(this, 'select', false);
});
}
}
}
}, "json");
}
// Handle component selection UI
$('#features-export-wrapper input[type=checkbox]:not(.processed)', context).addClass('processed').click(function() {
_resetTimeout();
if ($(this).hasClass('component-select')) {
moveCheckbox(this, 'added', true);
}
else if ($(this).hasClass('component-included')) {
moveCheckbox(this, 'added', false);
}
else if ($(this).hasClass('component-added')) {
if ($(this).is(':checked')) {
moveCheckbox(this, 'included', true);
}
else {
moveCheckbox(this, 'select', false);
}
}
});
// Handle select/unselect all
$('#features-filter .features-checkall', context).click(function() {
if ($(this).attr('checked')) {
_checkAll(true);
$(this).next().html(Drupal.t('Deselect all'));
}
else {
_checkAll(false);
$(this).next().html(Drupal.t('Select all'));
}
_resetTimeout();
});
// Handle filtering
// provide timer for auto-refresh trigger
var filterTimeoutID = 0;
var inFilterTimeout = 0;
function _triggerFilterTimeout() {
filterTimeoutID = 0;
_updateFilter();
}
function _resetFilterTimeout() {
inFilterTimeout++;
// if timeout is already active, reset it
if (filterTimeoutID != 0) {
window.clearTimeout(filterTimeoutID);
if (inFilterTimeout > 0) inFilterTimeout--;
}
filterTimeoutID = window.setTimeout(_triggerFilterTimeout, 200);
}
function _updateFilter() {
var filter = $('#features-filter input').val();
var regex = new RegExp(filter, 'i');
// collapse fieldsets
var newState = {};
var currentState = {};
$('#features-export-wrapper fieldset.features-export-component', context).each(function() {
// expand parent fieldset
var section = $(this).attr('id');
currentState[section] = !($(this).hasClass('collapsed'));
if (!(section in newState)) {
newState[section] = false;
}
$(this).find('div.component-select label').each(function() {
if (filter == '') {
if (currentState[section]) {
Drupal.toggleFieldset($('#'+section));
currentState[section] = false;
}
$(this).parent().show();
}
else if ($(this).text().match(regex)) {
$(this).parent().show();
newState[section] = true;
}
else {
$(this).parent().hide();
}
});
});
for (section in newState) {
if (currentState[section] != newState[section]) {
Drupal.toggleFieldset($('#'+section));
}
}
}
$('#features-filter input', context).bind("input", function() {
_resetFilterTimeout();
});
$('#features-filter .features-filter-clear', context).click(function() {
$('#features-filter input').val('');
_updateFilter();
});
// show the filter bar
$('#features-filter', context).removeClass('element-invisible');
}
}
Drupal.features = {
'checkStatus': function() {
$('table.features tbody tr').not('.processed').filter(':first').each(function() {
var elem = $(this);
$(elem).addClass('processed');
var uri = $(this).find('a.admin-check').attr('href');
if (uri) {
$.get(uri, [], function(data) {
$(elem).find('.admin-loading').hide();
switch (data.storage) {
case 3:
$(elem).find('.admin-rebuilding').show();
break;
case 2:
$(elem).find('.admin-needs-review').show();
break;
case 1:
$(elem).find('.admin-overridden').show();
break;
default:
$(elem).find('.admin-default').show();
break;
}
Drupal.features.checkStatus();
}, 'json');
}
else {
Drupal.features.checkStatus();
}
});
}
};
})(jQuery);
|