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