/*
 * jQuery UI Accordion 1.7.1
 *
 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Accordion
 *
 * Depends:
 *   ui.core.js
 */
(function($) {

$.widget("ui.accordion", {

   _init: function() {

      var o = this.options, self = this;
      this.running = 0;

      // if the user set the alwaysOpen option on init
      // then we need to set the collapsible option
      // if they set both on init, collapsible will take priority
      if (o.collapsible == $.ui.accordion.defaults.collapsible &&
         o.alwaysOpen != $.ui.accordion.defaults.alwaysOpen) {
         o.collapsible = !o.alwaysOpen;
      }

      if ( o.navigation ) {
         var current = this.element.find("a").filter(o.navigationFilter);
         if ( current.length ) {
            if ( current.filter(o.header).length ) {
               this.active = current;
            } else {
               this.active = current.parent().parent().prev();
               current.addClass("ui-accordion-content-active");
            }
         }
      }

      this.element.addClass("ui-accordion ui-widget ui-helper-reset");
      
      // in lack of child-selectors in CSS we need to mark top-LIs in a UL-accordion for some IE-fix
      if (this.element[0].nodeName == "UL") {
         this.element.children("li").addClass("ui-accordion-li-fix");
      }

      this.headers = this.element.find(o.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all")
         .bind("mouseenter.accordion", function(){ $(this).addClass('ui-state-hover'); })
         .bind("mouseleave.accordion", function(){ $(this).removeClass('ui-state-hover'); })
         .bind("focus.accordion", function(){ $(this).addClass('ui-state-focus'); })
         .bind("blur.accordion", function(){ $(this).removeClass('ui-state-focus'); });

      this.headers
         .next()
            .addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");

      this.active = this._findActive(this.active || o.active).toggleClass("ui-state-default").toggleClass("ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");
      this.active.next().addClass('ui-accordion-content-active');

      //Append icon elements
      $("<span/>").addClass("ui-icon " + o.icons.header).prependTo(this.headers);
      this.active.find(".ui-icon").toggleClass(o.icons.header).toggleClass(o.icons.headerSelected);

      // IE7-/Win - Extra vertical space in lists fixed
      if ($.browser.msie) {
         this.element.find('a').css('zoom', '1');
      }

      this.resize();

      //ARIA
      this.element.attr('role','tablist');

      this.headers
         .attr('role','tab')
         .bind('keydown', function(event) { return self._keydown(event); })
         .next()
         .attr('role','tabpanel');

      this.headers
         .not(this.active || "")
         .attr('aria-expanded','false')
         .attr("tabIndex", "-1")
         .next()
         .hide();

      // make sure at least one header is in the tab order
      if (!this.active.length) {
         this.headers.eq(0).attr('tabIndex','0');
      } else {
         this.active
            .attr('aria-expanded','true')
            .attr('tabIndex', '0');
      }

      // only need links in taborder for Safari
      if (!$.browser.safari)
         this.headers.find('a').attr('tabIndex','-1');

      if (o.event) {
         this.headers.bind((o.event) + ".accordion", function(event) { return self._clickHandler.call(self, event, this); });
      }

   },

   destroy: function() {
      var o = this.options;

      this.element
         .removeClass("ui-accordion ui-widget ui-helper-reset")
         .removeAttr("role")
         .unbind('.accordion')
         .removeData('accordion');

      this.headers
         .unbind(".accordion")
         .removeClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-corner-top")
         .removeAttr("role").removeAttr("aria-expanded").removeAttr("tabindex");

      this.headers.find("a").removeAttr("tabindex");
      this.headers.children(".ui-icon").remove();
      var contents = this.headers.next().css("display", "").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active");
      if (o.autoHeight || o.fillHeight) {
         contents.css("height", "");
      }
   },
   
   _setData: function(key, value) {
      if(key == 'alwaysOpen') { key = 'collapsible'; value = !value; }
      $.widget.prototype._setData.apply(this, arguments);   
   },

   _keydown: function(event) {

      var o = this.options, keyCode = $.ui.keyCode;

      if (o.disabled || event.altKey || event.ctrlKey)
         return;

      var length = this.headers.length;
      var currentIndex = this.headers.index(event.target);
      var toFocus = false;

      switch(event.keyCode) {
         case keyCode.RIGHT:
         case keyCode.DOWN:
            toFocus = this.headers[(currentIndex + 1) % length];
            break;
         case keyCode.LEFT:
         case keyCode.UP:
            toFocus = this.headers[(currentIndex - 1 + length) % length];
            break;
         case keyCode.SPACE:
         case keyCode.ENTER:
            return this._clickHandler({ target: event.target }, event.target);
      }

      if (toFocus) {
         $(event.target).attr('tabIndex','-1');
         $(toFocus).attr('tabIndex','0');
         toFocus.focus();
         return false;
      }

      return true;

   },

   resize: function() {

      var o = this.options, maxHeight;

      if (o.fillSpace) {
         
         if($.browser.msie) { var defOverflow = this.element.parent().css('overflow'); this.element.parent().css('overflow', 'hidden'); }
         maxHeight = this.element.parent().height();
         if($.browser.msie) { this.element.parent().css('overflow', defOverflow); }
   
         this.headers.each(function() {
            maxHeight -= $(this).outerHeight();
         });

         var maxPadding = 0;
         this.headers.next().each(function() {
            maxPadding = Math.max(maxPadding, $(this).innerHeight() - $(this).height());
         }).height(Math.max(0, maxHeight - maxPadding))
         .css('overflow', 'auto');

      } else if ( o.autoHeight ) {
         maxHeight = 0;
         this.headers.next().each(function() {
            maxHeight = Math.max(maxHeight, $(this).outerHeight());
         }).height(maxHeight);
      }

   },

   activate: function(index) {
      // call clickHandler with custom event
      var active = this._findActive(index)[0];
      this._clickHandler({ target: active }, active);
   },

   _findActive: function(selector) {
      return selector
         ? typeof selector == "number"
            ? this.headers.filter(":eq(" + selector + ")")
            : this.headers.not(this.headers.not(selector))
         : selector === false
            ? $([])
            : this.headers.filter(":eq(0)");
   },

   _clickHandler: function(event, target) {

      var o = this.options;
      if (o.disabled) return false;

      // called only when using activate(false) to close all parts programmatically
      if (!event.target && o.collapsible) {
         this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
            .find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
         this.active.next().addClass('ui-accordion-content-active');
         var toHide = this.active.next(),
            data = {
               options: o,
               newHeader: $([]),
               oldHeader: o.active,
               newContent: $([]),
               oldContent: toHide
            },
            toShow = (this.active = $([]));
         this._toggle(toShow, toHide, data);
         return false;
      }

      // get the click target
      var clicked = $(event.currentTarget || target);
      var clickedIsActive = clicked[0] == this.active[0];

      // if animations are still active, or the active header is the target, ignore click
      if (this.running || (!o.collapsible && clickedIsActive)) {
         return false;
      }

      // switch classes
      this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
         .find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
      this.active.next().addClass('ui-accordion-content-active');
      if (!clickedIsActive) {
         clicked.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top")
            .find(".ui-icon").removeClass(o.icons.header).addClass(o.icons.headerSelected);
         clicked.next().addClass('ui-accordion-content-active');
      }

      // find elements to show and hide
      var toShow = clicked.next(),
         toHide = this.active.next(),
         data = {
            options: o,
            newHeader: clickedIsActive && o.collapsible ? $([]) : clicked,
            oldHeader: this.active,
            newContent: clickedIsActive && o.collapsible ? $([]) : toShow.find('> *'),
            oldContent: toHide.find('> *')
         },
         down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );

      this.active = clickedIsActive ? $([]) : clicked;
      this._toggle(toShow, toHide, data, clickedIsActive, down);

      return false;

   },

   _toggle: function(toShow, toHide, data, clickedIsActive, down) {

      var o = this.options, self = this;

      this.toShow = toShow;
      this.toHide = toHide;
      this.data = data;

      var complete = function() { if(!self) return; return self._completed.apply(self, arguments); };

      // trigger changestart event
      this._trigger("changestart", null, this.data);

      // count elements to animate
      this.running = toHide.size() === 0 ? toShow.size() : toHide.size();

      if (o.animated) {

         var animOptions = {};

         if ( o.collapsible && clickedIsActive ) {
            animOptions = {
               toShow: $([]),
               toHide: toHide,
               complete: complete,
               down: down,
               autoHeight: o.autoHeight || o.fillSpace
            };
         } else {
            animOptions = {
               toShow: toShow,
               toHide: toHide,
               complete: complete,
               down: down,
               autoHeight: o.autoHeight || o.fillSpace
            };
         }

         if (!o.proxied) {
            o.proxied = o.animated;
         }

         if (!o.proxiedDuration) {
            o.proxiedDuration = o.duration;
         }

         o.animated = $.isFunction(o.proxied) ?
            o.proxied(animOptions) : o.proxied;

         o.duration = $.isFunction(o.proxiedDuration) ?
            o.proxiedDuration(animOptions) : o.proxiedDuration;

         var animations = $.ui.accordion.animations,
            duration = o.duration,
            easing = o.animated;

         if (!animations[easing]) {
            animations[easing] = function(options) {
               this.slide(options, {
                  easing: easing,
                  duration: duration || 700
               });
            };
         }

         animations[easing](animOptions);

      } else {

         if (o.collapsible && clickedIsActive) {
            toShow.toggle();
         } else {
            toHide.hide();
            toShow.show();
         }

         complete(true);

      }

      toHide.prev().attr('aria-expanded','false').attr("tabIndex", "-1").blur();
      toShow.prev().attr('aria-expanded','true').attr("tabIndex", "0").focus();

   },

   _completed: function(cancel) {

      var o = this.options;

      this.running = cancel ? 0 : --this.running;
      if (this.running) return;

      if (o.clearStyle) {
         this.toShow.add(this.toHide).css({
            height: "",
            overflow: ""
         });
      }

      this._trigger('change', null, this.data);
   }

});


$.extend($.ui.accordion, {
   version: "1.7.1",
   defaults: {
      active: null,
      alwaysOpen: true, //deprecated, use collapsible
      animated: 'slide',
      autoHeight: true,
      clearStyle: false,
      collapsible: false,
      event: "click",
      fillSpace: false,
      header: "> li > :first-child,> :not(li):even",
      icons: {
         header: "ui-icon-triangle-1-e",
         headerSelected: "ui-icon-triangle-1-s"
      },
      navigation: false,
      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;
         }
         if ( !options.toShow.size() ) {
            options.toHide.animate({height: "hide"}, options);
            return;
         }
         var overflow = options.toShow.css('overflow'),
            percentDone,
            showProps = {},
            hideProps = {},
            fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
            originalWidth;
         // fix width before calculating height of hidden element
         var s = options.toShow;
         originalWidth = s[0].style.width;
         s.width( parseInt(s.parent().width(),10) - parseInt(s.css("paddingLeft"),10) - parseInt(s.css("paddingRight"),10) - (parseInt(s.css("borderLeftWidth"),10) || 0) - (parseInt(s.css("borderRightWidth"),10) || 0) );
         
         $.each(fxAttrs, function(i, prop) {
            hideProps[prop] = 'hide';
            
            var parts = ('' + $.css(options.toShow[0], prop)).match(/^([\d+-.]+)(.*)$/);
            showProps[prop] = {
               value: parts[1],
               unit: parts[2] || 'px'
            };
         });
         options.toShow.css({ height: 0, overflow: 'hidden' }).show();
         options.toHide.filter(":hidden").each(options.complete).end().filter(":visible").animate(hideProps,{
            step: function(now, settings) {
               // only calculate the percent when animating height
               // IE gets very inconsistent results when animating elements
               // with small values, which is common for padding
               if (settings.prop == 'height') {
                  percentDone = (settings.now - settings.start) / (settings.end - settings.start);
               }
               
               options.toShow[0].style[settings.prop] =
                  (percentDone * showProps[settings.prop].value) + showProps[settings.prop].unit;
            },
            duration: options.duration,
            easing: options.easing,
            complete: function() {
               if ( !options.autoHeight ) {
                  options.toShow.css("height", "");
               }
               options.toShow.css("width", originalWidth);
               options.toShow.css({overflow: overflow});
               options.complete();
            }
         });
      },
      bounceslide: function(options) {
         this.slide(options, {
            easing: options.down ? "easeOutBounce" : "swing",
            duration: options.down ? 1000 : 200
         });
      },
      easeslide: function(options) {
         this.slide(options, {
            easing: "easeinout",
            duration: 700
         });
      }
   }
});

})(jQuery);
