/**
 * A hash of drop-downs by group name.
 */
var DropDownGroups = {};

/**
 * Repository of dropdowns.
 */
var DropDowns = {

  /**
   * An array of all registered dropdowns.
   */
  _all: new Array(),
  
  /**
   * Closes all currently open dropdowns.
   */
  close: function() {
    this.each(function(dropDown) {
      dropDown.close();
    });
  },
  
  /**
   * Registers a dropdown.
   */
  register: function(dropDown) {
    this._all[this._all.length] = dropDown;
  },
  
  /**
   * Invokes a callback for each registered dropdown.
   */
  each: function(callback) {
    return this._all.each(callback);
  }
  
};

/**
 * Dropdown class.
 */
var DropDown = Class.create();
DropDown.prototype = {
  
  /**
   * Creates a new dropdown. The link is the activating element. The panel is
   * the dropdown proper, which will be shown when the dropdown is opened.
   */
  initialize: function(link, panel, options) {  
    this.panel = $(panel);
    this.link = $(link);
    this.link.onclick = function() { this.toggle(); return false; }.bind(this); 
    this.group = options.group;
    this.position = options.position;
    this.onShow = options.onShow;
    this.onHide = options.onHide;
    DropDowns.register(this);
  },
  
  /**
   * Toggles the dropdown.
   */
  toggle: function() {
    this.isOpen() ? this.close() : this.open();
  },
  
  /**
   * Opens the dropdown.
   */
  open: function() {
    if (!this.isOpen()) {
      if (this.group) {
        var other = DropDownGroups[this.group];
        if (other) {
          other.close();
        }
        DropDownGroups[this.group] = this;
      }
      Element.addClassName(this.link, "active");
      if (this.position) {
        this.position(this.link, this.panel);
      }
      if (this.onShow) {
        this.onShow(this.panel);
      }
      else {
        new Effect.Appear(this.panel, {duration: 0.3, to: 0.9});
      }
    }
  },
  
  /**
   * Closes the dropdown.
   */
  close: function() {
    if (this.isOpen()) {
      if (this.onHide) {
        this.onHide(this.panel);
      }
      else {
        new Effect.Fade(this.panel, {duration: 0.3, from: 0.9});
      }
      Element.removeClassName(this.link, "active");
    }
  },
  
  /**
   * Returns true if the dropdown is open, otherwise false.
   */
  isOpen: function() {
    return Element.visible(this.panel);
  }

};

/**
 * Positioning function that puts the panel below, flush left.
 */
DropDown.Below = function(offsetX, offsetY) {
  if (!offsetX) offsetX = 0;
  if (!offsetY) offsetY = 0;
  return function(element, panel) {
    panel.style.position = "absolute";
    panel.style.top = (offsetY + element.offsetTop + element.offsetHeight - 1) + "px";
    panel.style.left = (offsetX + element.offsetLeft) + "px";
  }
}

/**
 * Positioning function that puts the panel below, flush right.
 */
DropDown.BelowFlushRight = function(offsetX, offsetY) {
  if (!offsetX) offsetX = 0;
  if (!offsetY) offsetY = 0;
  return function(element, panel) {
    panel.style.position = "absolute";
    panel.style.top = (offsetY + element.offsetTop + element.offsetHeight - 1) + "px";
    panel.style.left = (offsetX + element.offsetLeft + 
      element.offsetWidth - Element.getDimensions(panel)) + "px";
  }
}

/**
 * Make sure dropdowns close when user clicks outside it.
 */
Event.observe(document, "click", function(event) { 
  var element = Event.element(event);
  var hit = false;
  DropDowns.each(function(dropdown) {
    hit = hit || 
      element == dropdown.panel || 
      element == dropdown.link || 
      Element.childOf(element, dropdown.panel) ||
      Element.childOf(element, dropdown.link);
  })
  if (!hit) {
    DropDowns.close();
  }
});
