/**
 * 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 <td>'s parent (<tr>) will be sorted instead
 *   of the <td> 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);


