/*
 * jQuery UI Accordion 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Accordion
 *
 * Depends:
 *  jquery.ui.core.js
 *  jquery.ui.widget.js
 */
(function( $, undefined ) {

$.widget( "ui.accordion", {
  options: {
    active: 0,
    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();
    }
  },

  _create: function() {
    var self = this,
      options = self.options;

    self.running = 0;

    self.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
      .children( "li" )
        .addClass( "ui-accordion-li-fix" );

    self.headers = self.element.find( options.header )
      .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" )
      .bind( "mouseenter.accordion", function() {
        if ( options.disabled ) {
          return;
        }
        $( this ).addClass( "ui-state-hover" );
      })
      .bind( "mouseleave.accordion", function() {
        if ( options.disabled ) {
          return;
        }
        $( this ).removeClass( "ui-state-hover" );
      })
      .bind( "focus.accordion", function() {
        if ( options.disabled ) {
          return;
        }
        $( this ).addClass( "ui-state-focus" );
      })
      .bind( "blur.accordion", function() {
        if ( options.disabled ) {
          return;
        }
        $( this ).removeClass( "ui-state-focus" );
      });

    self.headers.next()
      .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" );

    if ( options.navigation ) {
      var current = self.element.find( "a" ).filter( options.navigationFilter ).eq( 0 );
      if ( current.length ) {
        var header = current.closest( ".ui-accordion-header" );
        if ( header.length ) {
          // anchor within header
          self.active = header;
        } else {
          // anchor within content
          self.active = current.closest( ".ui-accordion-content" ).prev();
        }
      }
    }

    self.active = self._findActive( self.active || options.active )
      .addClass( "ui-state-default ui-state-active" )
      .toggleClass( "ui-corner-all" )
      .toggleClass( "ui-corner-top" );
    self.active.next().addClass( "ui-accordion-content-active" );

    self._createIcons();
    self.resize();
    
    // ARIA
    self.element.attr( "role", "tablist" );

    self.headers
      .attr( "role", "tab" )
      .bind( "keydown.accordion", function( event ) {
        return self._keydown( event );
      })
      .next()
        .attr( "role", "tabpanel" );

    self.headers
      .not( self.active || "" )
      .attr({
        "aria-expanded": "false",
        "aria-selected": "false",
        tabIndex: -1
      })
      .next()
        .hide();

    // make sure at least one header is in the tab order
    if ( !self.active.length ) {
      self.headers.eq( 0 ).attr( "tabIndex", 0 );
    } else {
      self.active
        .attr({
          "aria-expanded": "true",
          "aria-selected": "true",
          tabIndex: 0
        });
    }

    // only need links in tab order for Safari
    if ( !$.browser.safari ) {
      self.headers.find( "a" ).attr( "tabIndex", -1 );
    }

    if ( options.event ) {
      self.headers.bind( options.event.split(" ").join(".accordion ") + ".accordion", function(event) {
        self._clickHandler.call( self, event, this );
        event.preventDefault();
      });
    }
  },

  _createIcons: function() {
    var options = this.options;
    if ( options.icons ) {
      $( "<span></span>" )
        .addClass( "ui-icon " + options.icons.header )
        .prependTo( this.headers );
      this.active.children( ".ui-icon" )
        .toggleClass(options.icons.header)
        .toggleClass(options.icons.headerSelected);
      this.element.addClass( "ui-accordion-icons" );
    }
  },

  _destroyIcons: function() {
    this.headers.children( ".ui-icon" ).remove();
    this.element.removeClass( "ui-accordion-icons" );
  },

  destroy: function() {
    var options = this.options;

    this.element
      .removeClass( "ui-accordion ui-widget ui-helper-reset" )
      .removeAttr( "role" );

    this.headers
      .unbind( ".accordion" )
      .removeClass( "ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
      .removeAttr( "role" )
      .removeAttr( "aria-expanded" )
      .removeAttr( "aria-selected" )
      .removeAttr( "tabIndex" );

    this.headers.find( "a" ).removeAttr( "tabIndex" );
    this._destroyIcons();
    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 ui-accordion-disabled ui-state-disabled" );
    if ( options.autoHeight || options.fillHeight ) {
      contents.css( "height", "" );
    }

    return $.Widget.prototype.destroy.call( this );
  },

  _setOption: function( key, value ) {
    $.Widget.prototype._setOption.apply( this, arguments );
      
    if ( key == "active" ) {
      this.activate( value );
    }
    if ( key == "icons" ) {
      this._destroyIcons();
      if ( value ) {
        this._createIcons();
      }
    }
    // #5332 - opacity doesn't cascade to positioned elements in IE
    // so we need to add the disabled class to the headers and panels
    if ( key == "disabled" ) {
      this.headers.add(this.headers.next())
        [ value ? "addClass" : "removeClass" ](
          "ui-accordion-disabled ui-state-disabled" );
    }
  },

  _keydown: function( event ) {
    if ( this.options.disabled || event.altKey || event.ctrlKey ) {
      return;
    }

    var keyCode = $.ui.keyCode,
      length = this.headers.length,
      currentIndex = this.headers.index( event.target ),
      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:
        this._clickHandler( { target: event.target }, event.target );
        event.preventDefault();
    }

    if ( toFocus ) {
      $( event.target ).attr( "tabIndex", -1 );
      $( toFocus ).attr( "tabIndex", 0 );
      toFocus.focus();
      return false;
    }

    return true;
  },

  resize: function() {
    var options = this.options,
      maxHeight;

    if ( options.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( true );
      });

      this.headers.next()
        .each(function() {
          $( this ).height( Math.max( 0, maxHeight -
            $( this ).innerHeight() + $( this ).height() ) );
        })
        .css( "overflow", "auto" );
    } else if ( options.autoHeight ) {
      maxHeight = 0;
      this.headers.next()
        .each(function() {
          maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
        })
        .height( maxHeight );
    }

    return this;
  },

  activate: function( index ) {
    // TODO this gets called on init, changing the option without an explicit call for that
    this.options.active = index;
    // call clickHandler with custom event
    var active = this._findActive( index )[ 0 ];
    this._clickHandler( { target: active }, active );

    return this;
  },

  _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)" );
  },

  // TODO isn't event.target enough? why the separate target argument?
  _clickHandler: function( event, target ) {
    var options = this.options;
    if ( options.disabled ) {
      return;
    }

    // called only when using activate(false) to close all parts programmatically
    if ( !event.target ) {
      if ( !options.collapsible ) {
        return;
      }
      this.active
        .removeClass( "ui-state-active ui-corner-top" )
        .addClass( "ui-state-default ui-corner-all" )
        .children( ".ui-icon" )
          .removeClass( options.icons.headerSelected )
          .addClass( options.icons.header );
      this.active.next().addClass( "ui-accordion-content-active" );
      var toHide = this.active.next(),
        data = {
          options: options,
          newHeader: $( [] ),
          oldHeader: options.active,
          newContent: $( [] ),
          oldContent: toHide
        },
        toShow = ( this.active = $( [] ) );
      this._toggle( toShow, toHide, data );
      return;
    }

    // get the click target
    var clicked = $( event.currentTarget || target ),
      clickedIsActive = clicked[0] === this.active[0];

    // TODO the option is changed, is that correct?
    // TODO if it is correct, shouldn't that happen after determining that the click is valid?
    options.active = options.collapsible && clickedIsActive ?
      false :
      this.headers.index( clicked );

    // if animations are still active, or the active header is the target, ignore click
    if ( this.running || ( !options.collapsible && clickedIsActive ) ) {
      return;
    }

    // find elements to show and hide
    var active = this.active,
      toShow = clicked.next(),
      toHide = this.active.next(),
      data = {
        options: options,
        newHeader: clickedIsActive && options.collapsible ? $([]) : clicked,
        oldHeader: this.active,
        newContent: clickedIsActive && options.collapsible ? $([]) : toShow,
        oldContent: toHide
      },
      down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );

    // when the call to ._toggle() comes after the class changes
    // it causes a very odd bug in IE 8 (see #6720)
    this.active = clickedIsActive ? $([]) : clicked;
    this._toggle( toShow, toHide, data, clickedIsActive, down );

    // switch classes
    active
      .removeClass( "ui-state-active ui-corner-top" )
      .addClass( "ui-state-default ui-corner-all" )
      .children( ".ui-icon" )
        .removeClass( options.icons.headerSelected )
        .addClass( options.icons.header );
    if ( !clickedIsActive ) {
      clicked
        .removeClass( "ui-state-default ui-corner-all" )
        .addClass( "ui-state-active ui-corner-top" )
        .children( ".ui-icon" )
          .removeClass( options.icons.header )
          .addClass( options.icons.headerSelected );
      clicked
        .next()
        .addClass( "ui-accordion-content-active" );
    }

    return;
  },

  _toggle: function( toShow, toHide, data, clickedIsActive, down ) {
    var self = this,
      options = self.options;

    self.toShow = toShow;
    self.toHide = toHide;
    self.data = data;

    var complete = function() {
      if ( !self ) {
        return;
      }
      return self._completed.apply( self, arguments );
    };

    // trigger changestart event
    self._trigger( "changestart", null, self.data );

    // count elements to animate
    self.running = toHide.size() === 0 ? toShow.size() : toHide.size();

    if ( options.animated ) {
      var animOptions = {};

      if ( options.collapsible && clickedIsActive ) {
        animOptions = {
          toShow: $( [] ),
          toHide: toHide,
          complete: complete,
          down: down,
          autoHeight: options.autoHeight || options.fillSpace
        };
      } else {
        animOptions = {
          toShow: toShow,
          toHide: toHide,
          complete: complete,
          down: down,
          autoHeight: options.autoHeight || options.fillSpace
        };
      }

      if ( !options.proxied ) {
        options.proxied = options.animated;
      }

      if ( !options.proxiedDuration ) {
        options.proxiedDuration = options.duration;
      }

      options.animated = $.isFunction( options.proxied ) ?
        options.proxied( animOptions ) :
        options.proxied;

      options.duration = $.isFunction( options.proxiedDuration ) ?
        options.proxiedDuration( animOptions ) :
        options.proxiedDuration;

      var animations = $.ui.accordion.animations,
        duration = options.duration,
        easing = options.animated;

      if ( easing && !animations[ easing ] && !$.easing[ easing ] ) {
        easing = "slide";
      }
      if ( !animations[ easing ] ) {
        animations[ easing ] = function( options ) {
          this.slide( options, {
            easing: easing,
            duration: duration || 700
          });
        };
      }

      animations[ easing ]( animOptions );
    } else {
      if ( options.collapsible && clickedIsActive ) {
        toShow.toggle();
      } else {
        toHide.hide();
        toShow.show();
      }

      complete( true );
    }

    // TODO assert that the blur and focus triggers are really necessary, remove otherwise
    toHide.prev()
      .attr({
        "aria-expanded": "false",
        "aria-selected": "false",
        tabIndex: -1
      })
      .blur();
    toShow.prev()
      .attr({
        "aria-expanded": "true",
        "aria-selected": "true",
        tabIndex: 0
      })
      .focus();
  },

  _completed: function( cancel ) {
    this.running = cancel ? 0 : --this.running;
    if ( this.running ) {
      return;
    }

    if ( this.options.clearStyle ) {
      this.toShow.add( this.toHide ).css({
        height: "",
        overflow: ""
      });
    }

    // other classes are removed before the animation; this one needs to stay until completed
    this.toHide.removeClass( "ui-accordion-content-active" );
    // Work around for rendering bug in IE (#5421)
    if ( this.toHide.length ) {
      this.toHide.parent()[0].className = this.toHide.parent()[0].className;
    }

    this._trigger( "change", null, this.data );
  }
});

$.extend( $.ui.accordion, {
  version: "1.8.16",
  animations: {
    slide: function( options, additions ) {
      options = $.extend({
        easing: "swing",
        duration: 300
      }, options, additions );
      if ( !options.toHide.size() ) {
        options.toShow.animate({
          height: "show",
          paddingTop: "show",
          paddingBottom: "show"
        }, options );
        return;
      }
      if ( !options.toShow.size() ) {
        options.toHide.animate({
          height: "hide",
          paddingTop: "hide",
          paddingBottom: "hide"
        }, options );
        return;
      }
      var overflow = options.toShow.css( "overflow" ),
        percentDone = 0,
        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.end - settings.start === 0 ) ? 0 :
              ( 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,
            overflow: overflow
          });
          options.complete();
        }
      });
    },
    bounceslide: function( options ) {
      this.slide( options, {
        easing: options.down ? "easeOutBounce" : "swing",
        duration: options.down ? 1000 : 200
      });
    }
  }
});

})( jQuery );

