/**
 * SelectableList allows you to create a selectable list from any DOM elements on the page.
 * Simply pass the name of you list and the elements that will comprise the list to the 
 * contstructor.  
 * 
 * There are a few events dispatched through the EventManager that you should be aware of.
 * All events dispatched from SelectableList are prefixed with the SelectableList's
 * name.  For example, if I were to name my list "NatesList" the event names would be as follows:
 * 
 * SelectableList: 
 * note: whenever a bulk select/deselect is being performed the individual 
 * "ItemSelected"/"ItemDeselected" events are not fired.
 * - NatesListAllSelected - occurs after "selectAll" is executed 
 * - NatesListAllDeselected - occurs after "deselectAll" is executed
 * Particpating elements:
 * - NatesListItemSelected - occurs after "selectSelf" is executed
 * - NatesListItemDeselected - occurs after "deselectSelf" is executed
 * 
 * Whenever an element is selected, its "isSelected" property is set to true.
 * 
 * SelectableList extends the elements that participate in the list with the following methods:
 * - toggleSelection : toggles the selection
 * - selectSelf : selects the element
 * - deselectSelf : deselects the element
 * 
 * Whenever an element is selected, an additional CSS class name of "selected" is assigned to the element.
 * 
 * SelectableList derrives some additional functionality from its super class of SimpleList.
 */
var SelectableList = Class.create(SimpleList, { 
  
  /**
   * Constructor...
   * Note: the "name" must be unique because it is included in triggered event names. 
   */
  initialize: function($super, name, elements) {
    $super(name, elements);
    
    var len = this.elements.length;
    for (var i = 0; i < len; i++) {
      var el = this.elements[i];
      el.list = this;
      var count = this.extensionMethods.names.length;
      
      for (var x = 0; x < count; x++) {
        var name = this.extensionMethods.names[x];
        el[name] = this.extensionMethods[name];
      }
      
      el.observe("click", el.toggleSelection);  
    }
  },
  
  /**
   * Methods attached to each DOM node participating in the SelectableList.
   */
  extensionMethods: {
    names: ["selectSelf", "deselectSelf", "toggleSelection"],
    
    /** Toggles the selection. */
    toggleSelection: function() {
      if (this.isSelected) {
        this.deselectSelf();
      } else {
        this.selectSelf();
      }
    },
    
    /** 
     * Selects the element.
     * Fires the "[ListName]ItemSelected" event after the operation completes.
     */
    selectSelf: function() {
      if (this.style.display != "none" && !this.isSelected) {
        this.isSelected = true;
        this.addClassName("selected");
        if (!this.list.selectAllInProgress) {
          EventManager.fireEvent(this.list.name + "ItemSelected", this);
        }
      }
    },

    /** 
     * Deselects the element.
     * Fires the "[ListName]ItemDeselected" event after the operation completes.
     */
    deselectSelf: function() {
      if (this.isSelected) {
        this.isSelected = false;
        this.removeClassName("selected");
        if (!this.list.deselectAllInProgress) {
          EventManager.fireEvent(this.list.name + "ItemDeselected", this);
        }
      }
    }
  },

  /**
   * Selects all elements in the list.
   * Fires the "[ListName]AllSelected" event after the operation completes.
   */
  selectAll: function() {
    this.selectAllInProgress = true;
    this.elements.invoke("selectSelf");
    this.selectAllInProgress = false;
    EventManager.fireEvent(this.name + "AllSelected", this);
  },
  selectAllItems: this.selectAll,
  
  /**
   * Deselects all elements in the list.
   * Fires the "[ListName]AllDeselected" event after the operation completes.
   */
  deselectAll: function() {
    this.deselectAllInProgress = true;
    this.elements.invoke("deselectSelf");
    this.deselectAllInProgress = false;
    EventManager.fireEvent(this.name + "AllDeselected", this);
  },
  deselectAllItems: this.deselectAll,
  
  /**
   * Gets all selected elements.
   */
  getSelected: function() {
    var list = [];
    var len = this.elements.length;
    
    for (var i = 0; i < len; i++) {
      var el = this.elements[i];
      if (el.isSelected) {
        list.push(el);
      }
    }
    
    return list;
  },
  getSelectedItems: this.getSelected, 
  
  /**
   * Gets all deselected elements.
   */
  getDeselected: function() {
    var list = [];
    var len = this.elements.length;
    
    for (var i = 0; i < len; i++) {
      var el = this.elements[i];
      if (!el.isSelected) {
        list.push(el);
      }
    }
    
    return list;
  },
  getDeselectedItems: this.getDeselected,
  
  /**
   * Gets all selected elements that have a matching atribute name/value pair.
   * @param {String} attrName The attribute name to find.
   * @param {Object} attrValue The attribute value to find.
   */
  getSelectedItemsWithAttribute: function(attrName, attrValue) {
    var list = [];
    var elementsWith = this.getItemsWithAttribute(attrName, attrValue);
    var len = elementsWith.length;
    
    for (var i = 0; i < len; i++) {
      var el = elementsWith[i];
      if (el.isSelected) {
        list.push(el);
      }
    }
    
    return list;
  },
  getSelectedItemsWithAttr: this.getSelectedItemsWithAttribute,
  getSelectedWithAttr: this.getSelectedItemsWithAttribute,
  
  /**
   * Gets all deselected elements that have a matching atribute name/value pair.
   * @param {String} attrName The attribute name to find.
   * @param {Object} attrValue The attribute value to find.
   */
  getDeselectedItemsWithAttribute: function(attrName, attrValue) {
    var list = [];
    var elementsWith = this.getItemsWithAttribute(attrName, attrValue);
    var len = elementsWith.length;
    
    for (var i = 0; i < len; i++) {
      var el = elementsWith[i];
      if (!el.isSelected) {
        list.push(el);
      }
    }
    
    return list;
  },
  getDeselectedItemsWithAttr: this.getDeselectedItemsWithAttribute,
  getDeselectedWithAttr: this.getDeselectedItemsWithAttribute,
  
  /**
   * Selects all elements that have a matching atribute name/value pair.
   * @param {String} attrName The attribute name to find.
   * @param {Object} attrValue The attribute value to find.
   */
  selectItemsWithAttribute: function(attrName, attrValue) {
    var list = [];
    var elementsWith = this.getItemsWithAttribute(attrName, attrValue);
    var len = elementsWith.length;
    
    for (var i = 0; i < len; i++) {
      var el = elementsWith[i];
      el.selectSelf();
    }
    
    return list;
  },
  selectItemsWithAttr: this.selectItemsWithAttribute,
  selectWithAttr: this.selectItemsWithAttribute,
  
  /**
   * Deselects all elements that have a matching atribute name/value pair.
   * @param {String} attrName The attribute name to find.
   * @param {Object} attrValue The attribute value to find.
   */
  deselectItemsWithAttribute: function(attrName, attrValue) {
    var list = [];
    var elementsWith = this.getItemsWithAttribute(attrName, attrValue);
    var len = elementsWith.length;
    
    for (var i = 0; i < len; i++) {
      var el = elementsWith[i];
      el.deselectSelf();
    }
    
    return list;
  },
  deselectItemsWithAttr: this.deselectItemsWithAttribute,
  deselectWithAttr: this.deselectItemsWithAttribute,
  
  /**
   * Gets an attribute value from all selected elements.
   * @param {String} attrName The attribute name to find.
   */
  getAttributeFromSelectedItems: function(attrName) {
    var list = [];
    var elements = this.getSelected();
    var len = elements.length;
    
    for (var i = 0; i < len; i++) {
      var el = elements[i];
      list.push(el.getAttributeValue(attrName))
    }
    
    return list;
  },
  getAttrFromSelectedItems: this.getAttributeFromSelectedItems,
  getAttrFromSelected: this.getAttributeFromSelectedItems,
  
  /**
   * Gets an attribute value from all deselected elements.
   * @param {String} attrName The attribute name to find.
   */
  getAttributeFromDeselectedItems: function(attrName) {
    var list = [];
    var elements = this.getDeselected();
    var len = elements.length;
    
    for (var i = 0; i < len; i++) {
      var el = elements[i];
      list.push(el.getAttributeValue(attrName))
    }
    
    return list;
  },
  getAttrFromDeselectedItems: this.getAttributeFromDeselectedItems,
  getAttrFromDeselected: this.getAttributeFromDeselectedItems

});
