/*
 * jQuery UI Accordion 1.6
 * 
 * Copyright (c) 2007 Jörn Zaefferer
 *
 * http://docs.jquery.com/UI/Accordion
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: ui.accordion.js,v 1.2 2008-11-19 13:16:55 mdeumlich Exp $
 *
 */

;(function($) {
  
// If the UI scope is not available, add it
$.ui = $.ui || {};

$.fn.extend({
  accordion: function(options, data) {
    var args = Array.prototype.slice.call(arguments, 1);

    return this.each(function() {
      if (typeof options == "string") {
        var accordion = $.data(this, "ui-accordion");
        accordion[options].apply(accordion, args);
      // INIT with optional options
      } else if (!$(this).is(".ui-accordion"))
        $.data(this, "ui-accordion", new $.ui.accordion(this, options));
    });
  },
  // deprecated, use accordion("activate", index) instead
  activate: function(index) {
    return this.accordion("activate", index);
  }
});

$.ui.accordion = function(container, options) {
  
  // setup configuration
  this.options = options = $.extend({}, $.ui.accordion.defaults, options);
  this.element = container;
  
  $(container).addClass("ui-accordion");
  
  if ( options.navigation ) {
    var current = $(container).find("a").filter(options.navigationFilter);
    if ( current.length ) {
      if ( current.filter(options.header).length ) {
        options.active = current;
      } else {
        options.active = current.parent().parent().prev();
        current.addClass("current");
      }
    }
  }
  
  // calculate active if not specified, using the first header
  options.headers = $(container).find(options.header);
  options.active = findActive(options.headers, options.active);

  if ( options.fillSpace ) {
    var maxHeight = $(container).parent().height();
    options.headers.each(function() {
      maxHeight -= $(this).outerHeight();
    });
    var maxPadding = 0;
    options.headers.next().each(function() {
      maxPadding = Math.max(maxPadding, $(this).innerHeight() - $(this).height());
    }).height(maxHeight - maxPadding);
  } else if ( options.autoheight ) {
    var maxHeight = 0;
    options.headers.next().each(function() {
      maxHeight = Math.max(maxHeight, $(this).outerHeight());
    }).height(maxHeight);
  }

  options.headers
    .not(options.active || "")
    .next()
    .hide();
  options.active.parent().andSelf().addClass(options.selectedClass);
  
  if (options.event)
    $(container).bind((options.event) + ".ui-accordion", clickHandler);
};

$.ui.accordion.prototype = {
  activate: function(index) {
    // call clickHandler with custom event
    clickHandler.call(this.element, {
      target: findActive( this.options.headers, index )[0]
    });
  },
  
  enable: function() {
    this.options.disabled = false;
  },
  disable: function() {
    this.options.disabled = true;
  },
  destroy: function() {
    this.options.headers.next().css("display", "");
    if ( this.options.fillSpace || this.options.autoheight ) {
      this.options.headers.next().css("height", "");
    }
    $.removeData(this.element, "ui-accordion");
    $(this.element).removeClass("ui-accordion").unbind(".ui-accordion");
  }
}

function scopeCallback(callback, scope) {
  return function() {
    return callback.apply(scope, arguments);
  };
}

function completed(cancel) {
  // if removed while animated data can be empty
  if (!$.data(this, "ui-accordion"))
    return;
  var instance = $.data(this, "ui-accordion");
  var options = instance.options;
  options.running = cancel ? 0 : --options.running;
  if ( options.running )
    return;
  if ( options.clearStyle ) {
    options.toShow.add(options.toHide).css({
      height: "",
      overflow: ""
    });
  }
  $(this).triggerHandler("change.ui-accordion", [options.data], options.change);
}

function toggle(toShow, toHide, data, clickedActive, down) {
  var options = $.data(this, "ui-accordion").options;
  options.toShow = toShow;
  options.toHide = toHide;
  options.data = data;
  var complete = scopeCallback(completed, this);
  
  // count elements to animate
  options.running = toHide.size() == 0 ? toShow.size() : toHide.size();
  
  if ( options.animated ) {
    if ( !options.alwaysOpen && clickedActive ) {
      $.ui.accordion.animations[options.animated]({
        toShow: jQuery([]),
        toHide: toHide,
        complete: complete,
        down: down,
        autoheight: options.autoheight
      });
    } else {
      $.ui.accordion.animations[options.animated]({
        toShow: toShow,
        toHide: toHide,
        complete: complete,
        down: down,
        autoheight: options.autoheight
      });
    }
  } else {
    if ( !options.alwaysOpen && clickedActive ) {
      toShow.toggle();
    } else {
      toHide.hide();
      toShow.show();
    }
    complete(true);
  }
}

function clickHandler(event) {
  var options = $.data(this, "ui-accordion").options;
  if (options.disabled)
    return false;
  
  // called only when using activate(false) to close all parts programmatically
  if ( !event.target && !options.alwaysOpen ) {
    options.active.parent().andSelf().toggleClass(options.selectedClass);
    var toHide = options.active.next(),
      data = {
        instance: this,
        options: options,
        newHeader: jQuery([]),
        oldHeader: options.active,
        newContent: jQuery([]),
        oldContent: toHide
      },
      toShow = options.active = $([]);
    toggle.call(this, toShow, toHide, data );
    return false;
  }
  // get the click target
  var clicked = $(event.target);
  
  // due to the event delegation model, we have to check if one
  // of the parent elements is our actual header, and find that
  if ( clicked.parents(options.header).length )
    while ( !clicked.is(options.header) )
      clicked = clicked.parent();
  
  var clickedActive = clicked[0] == options.active[0];
  
  // if animations are still active, or the active header is the target, ignore click
  if (options.running || (options.alwaysOpen && clickedActive))
    return false;
  if (!clicked.is(options.header))
    return;

  // switch classes
  options.active.parent().andSelf().toggleClass(options.selectedClass);
  if ( !clickedActive ) {
    clicked.parent().andSelf().addClass(options.selectedClass);
  }

  // find elements to show and hide
  var toShow = clicked.next(),
    toHide = options.active.next(),
    //data = [clicked, options.active, toShow, toHide],
    data = {
      instance: this,
      options: options,
      newHeader: clicked,
      oldHeader: options.active,
      newContent: toShow,
      oldContent: toHide
    },
    down = options.headers.index( options.active[0] ) > options.headers.index( clicked[0] );
  
  options.active = clickedActive ? $([]) : clicked;
  toggle.call(this, toShow, toHide, data, clickedActive, down );

  return false;
};

function findActive(headers, selector) {
  return selector != undefined
    ? typeof selector == "number"
      ? headers.filter(":eq(" + selector + ")")
      : headers.not(headers.not(selector))
    : selector === false
      ? $([])
      : headers.filter(":eq(0)");
}

$.extend($.ui.accordion, {
  defaults: {
    selectedClass: "selected",
    alwaysOpen: true,
    animated: 'slide',
    event: "click",
    header: "a",
    autoheight: true,
    running: 0,
    navigationFilter: function() {
      return this.href.toLowerCase() == location.href.toLowerCase();
    }
  },
  animations: {
    slide: function(options, additions) {
      options = $.extend({
        easing: "swing",
        duration: 300
      }, options, additions);
      if ( !options.toHide.size() ) {
        options.toShow.animate({height: "show"}, options);
        return;
      }
      var hideHeight = options.toHide.height(),
        showHeight = options.toShow.height(),
        difference = showHeight / hideHeight;
      options.toShow.css({ height: 0, overflow: 'hidden' }).show();
      options.toHide.filter(":hidden").each(options.complete).end().filter(":visible").animate({height:"hide"},{
        step: function(now) {
          var current = (hideHeight - now) * difference;
          if ($.browser.msie || $.browser.opera) {
            current = Math.ceil(current);
          }
          options.toShow.height( current );
        },
        duration: options.duration,
        easing: options.easing,
        complete: function() {
          if ( !options.autoheight ) {
            options.toShow.css("height", "auto");
          }
          options.complete();
        }
      });
    },
    bounceslide: function(options) {
      this.slide(options, {
        easing: options.down ? "bounceout" : "swing",
        duration: options.down ? 1000 : 200
      });
    },
    easeslide: function(options) {
      this.slide(options, {
        easing: "easeinout",
        duration: 700
      })
    }
  }
});

})(jQuery);
