/*
  Needs the Flash player object to implement the following methods:
   * stop() -> last position played
   * play(url, position) -> start playing an MP3 at given position
   * position() -> get current position
   * length() -> get length of MP3
   * getVolume() -> get current volume level
   * setVolume(level) -> set new volume level
*/
(function(_jQuery) {
  // Copy out jquery object
  $ = _jQuery
  // Note: jQuery UI library is added at the end of this closure

  // CONSTANTS
  var SCRUBBER_WIDTH = 340;
  
  var VOLUME_TOP = -58;
  var VOLUME_BOTTOM = -16;
  var VOLUME_HEIGHT = VOLUME_BOTTOM - VOLUME_TOP;
  var VOLUME_EPSILON = 0.1;
  
  this.SermonControl = function(container, sermonsPerPage, sermons, playerObject) {

    // The Flash player object
    this.player = playerObject; // The DOM object/embed which is our Flash MP3 player...

    // Master container
    this.control = container;
    
    // Sermon-list container
    this.container = $(this.control).find('.mainPanel')[0];
    
    // Sermon data
    this.allSermons = sermons;
    this.sermons = sermons;  
    this.sermonsPerPage = sermonsPerPage;

    // Paging stuff
    this.pages = this.countPages();
    this.currentPage = 0;
    this.pageSpan = $(this.control).find('.pageNumbers')[0];
    
    // Volume control stuff
    this.vWrap = $(this.control).find('.screenWrapper');
    this.vBox = $(this.control).find('.volumeBox');
    this.vWrap.click(this.hideWrap_factory());  // Always hide the volume if the screen wrapper is clicked
    this.vWrap.mouseenter(this.hideWrap_factory()); // ...or hovered
    
    // Helper for moving the volume bar to the right position
    var volumeCalculator = function(volumeBox, clientY) {
      var btop = $(volumeBox).offset().top;
      var spot = (clientY - btop) + VOLUME_TOP - 10;
      var volume = 1 - (spot - VOLUME_TOP) / VOLUME_HEIGHT;
      var clamped = Math.min(Math.max(0, volume), 1);
      return clamped;
    };
    
    // Helper for setting the slider handle to the right position
    var volumeUpdate = function(volumeBox, volume) {
      var offset = VOLUME_BOTTOM - (VOLUME_HEIGHT * volume);
      $(volumeBox).find('.volumeHandle').css('top', offset);
    };
    
    // Add the volume-bar-'drag' event handlers
    this.vBox.mousedown((function(self) {
        return function(e) {  // Start drage
          var v;
          
          // Move the Volume bar to the correct INITIAL position
          v = volumeCalculator(this, e.clientY);
          volumeUpdate(this, v);
          self.player.setVolume(v);
          self._lastVolume = v;
          
          $(this).mousemove(function(e) { // Register mover handler
            // Keep moving the volume bar to the correct position
            v = volumeCalculator(this, e.clientY);
            volumeUpdate(this, v);
            self._lastVolume = v;
          });
        };
      })(this)).mouseup((function(self) {
        return function() { // Stop drag by releasing button
          self.player.setVolume(self._lastVolume);
          $(this).unbind('mousemove');  // Clear the move handler
        };
      })(this));
    
    // Playing/pausing/etc. stuff
    this.activeSermon = null;
    this.lastUrl = '';
    this.lastPosition = -1;
    this.activeLength = 0;
    this.activeSeconds = 0;
    this.timer = null;
    this.lastPosUpdate = -1;
    this.areDraggingScrubber = false;

    // Filter stuff
    // Initialize jquery plugin
    $("div.filters").accordion({header: ".filterHeader"});
    // Set default
    this.filtering = ["Speaker", "All"];
  };
  
  this.SermonControl.prototype = {

    // Event handle factory functions
    hideWrap_factory: function() {
      var self = this;
      return function() {
        // Hide the volume stuff
        self.vWrap.hide();
        self.vBox.hide();
      };
    },
    
    showVolume_factory: function() {
      var self = this;
      return function() {
        // Calculate the position of our volume control
        var pos = $(this).offset();
        self.vBox.css({
          left: pos.left - 5,
          top: pos.top - 40
        });
        
        // Get the player volume and move the slider accordingly
        var offset = VOLUME_BOTTOM - (VOLUME_HEIGHT * self.player.getVolume());
        $(self.vBox).find('.volumeHandle').css('top', offset);
        
        // Show the volume stuff
        self.vWrap.show();
        self.vBox.show();
      };
    },
    
    // Other functions
    render: function() {

      this.container.innerHTML = '';
      
      for (i = this.currentPage * this.sermonsPerPage; 
           i < (this.currentPage + 1) * this.sermonsPerPage && this.sermons[i] ; i++) {
        
        var s = this.sermons[i];
        
        // Make skeleton
        var box = this.newElement('div', 'sermonBox', '', this.container);
        box.id = 'sermon' + s.SermonID;
        var leftHeader = this.newElement('div', 'leftHeader', '', box);
        var rightHeader = this.newElement('div', 'rightHeader', '', box);
        var sermonPlayer = this.newElement('div', 'sermonPlayer', '', box);
        
        // Add left-header junk
        this.newElement('p', 'sermonTitle', s.Title, leftHeader);
        this.newElement('p', 'sermonSpeaker', s.Speaker, leftHeader);
        this.newElement('p', 'sermonScripture', s.ParsedText, leftHeader);
        
        // And right-header junk
        this.newElement('p', 'sermonDate', s.ParsedDate, rightHeader);
        var sermonLinks = this.newElement('p', 'sermonLinks', '', rightHeader);
        var downloadLink = this.newElement('a', '', 'Download MP3', sermonLinks);
        downloadLink.href = s.MP3Filename;
        
        // Create player junk
        sermonPlayer.appendChild(this.makePlayer(s));
        
        // Highlight the active sermon
        if (s.SermonID == this.activeSermon) {
          $(box).addClass('activeSermon');
          this.updateProgress(this);
        }
      }
      
      this.pageSpan.innerHTML = "Page " + (this.currentPage + 1) + " of " + this.pages;
      
      var next = $(this.control).find('.nextPage a')[0];
      var prev = $(this.control).find('.previousPage a')[0];
      if (this.currentPage == 0) {
        prev.className = "inactive";
      } else {
        prev.className = "";
      }
      
      if (this.currentPage < this.pages - 1) {
        next.className = "";
      } else {
        next.className = "inactive";
      }
    },
    
    updateTimestamp: function(ratioDone) {
      var secs = parseInt(this.activeSeconds * ratioDone);
      var mins = parseInt(secs / 60);
      secs = secs % 60;
      var tstring = [(mins < 10) ? '0' : '',
                     mins.toFixed(),
                     ':',
                     (secs < 10) ? '0' : '',
                     secs.toFixed()].join('');
      $('#sermon' + this.activeSermon + ' .sermonTimeStamp').text(tstring);
    },
    
    updateProgress: function(self, pos) {
      if (pos == undefined) { pos = self.player.aposition(); }
      var ratio = pos / self.activeLength;
      
      if (!self.areDraggingScrubber) {
        $('#sermon' + self.activeSermon + ' .sermonScrubberBar').
          css('left', ratio * SCRUBBER_WIDTH);
        self.updateTimestamp(ratio);
      }
    },
    
    manualUpdate: function(ratioDone) {
      // Calculate new position (estimated)
      var newPos = this.activeLength * ratioDone
      this.updateTimestamp(ratioDone);
      
      // Are we paused?
      if (this.lastPosition > -1) {
        // Just update the position for play-resumption
        this.lastPosition = newPos;
      } else {
        // seek to the new position
        this.player.aseek(newPos);
      }
    },
    
    startPlaying: function(sermon) {
      if (this.activeSermon == sermon.SermonID) {  // PAUSE and UNPAUSE
        // Is the player currently paused?
        if (this.lastPosition >= 0) {
          // Un-pause it
          this.player.aplay(this.lastUrl, this.lastPosition);
          this.lastPosition = -1;
          
          // Set up the progress-update timer
          var self = this;
          this.timer = window.setInterval(function() { self.updateProgress(self); }, 1000);
        } else {
          // Pause it
          this.lastPosition = this.player.astop();
          
          // Stop the progress-update timer
          window.clearInterval(this.timer);
        }
      } else {  // PLAY
        // Stop any currently playing sermon.
        if (this.activeSermon) { this.stopPlaying(); }
        
        // Load/play the new MP3
        this.player.aplay(sermon.MP3Filename, 0);
        
        // Record the new active sermon
        this.activeSermon = sermon.SermonID;
        this.lastUrl = sermon.MP3Filename;
        this.activeLength = parseInt(sermon.MP3Duration) * 60000;  // Minutes-to-milliseconds
        this.activeSeconds = this.activeLength / 1000;             // But we still need seconds, too...
        this.lastPosition = -1;
        
        // Do the first scrubber-bar update
        this.updateProgress(this);
        
        // Set up the progress-update timer
        var self = this;
        this.timer = window.setInterval(function() { self.updateProgress(self); }, 1000);
        
        // Clear out all other active panels/buttons
        $('.activeSermon').removeClass('activeSermon');
        
        // Mark the appropriate panel
        $('#sermon' + sermon.SermonID).addClass('activeSermon');
      }
    },
    
    stopPlaying: function(sermon) {
      // Stop that sucker
      this.player.astop();
      this.updateProgress(this, 0);
      
      // Stop the progress-update timer
      window.clearInterval(this.timer);
      
      // Restore the GUI to all-inactive state
      $('.activeSermon').removeClass('activeSermon');
      
      // Clear out the active sermon
      this.activeSermon = null;
      this.lastPosition = -1;
    },
    
    makePlayer: function(sermon) {
      var template = $([
        '<div class="sermonPlayer">',
        '  <div class="canPlay">',
        '    <div class="play button"><img src="images/play.png" alt="Play" title="Play" /></div>',
        '    <span class="listenOnlineBlurb">Listen Online</span>',
        '  </div>',
        '  <div class="isPlaying">',
        '    <div class="sermonControls">',
        '      <div class="pause button"><img src="images/pause.png" alt="Pause/Resume" title="Pause/Resume" /></div>',
        '      <div class="vol button"><img src="images/vol.png" alt="Volume" title="Volume" /></div>',
        '      <div class="sermonScrubberBox">',
        '        <div class="sermonTimeStamp">00:00</div>',
        '        <div class="sermonScrubberLine"> </div>',
        '        <div class="sermonScrubberBar"> </div>',
        '      </div>',
        '    </div>',
        '  </div>',
        '</div>'
      ].join('\n'));
      
      // Add play/pause event handler
      var handler = (function(self, sermon) {
        return function() {
          self.startPlaying(sermon);
        };
      })(this, sermon);
      template.find('.play').click(handler);
      template.find('.pause').click(handler); // Safari 3 is retarded and breaks Sizzle, so I have to do 2 separate .find() calls
      
      // Add volume popup event
      template.find('.vol').click(this.showVolume_factory());
      
      // Helper for moving the scrubber bar to the right position
      var scrubHelper = function(scrubberBox, clientX) {
        return Math.min(Math.max(0, clientX - $(scrubberBox).offset().left - 10), SCRUBBER_WIDTH);
      };
      
      // Add scrubber-bar-"drag" event handlers
      var self = this;
      template.find('.sermonScrubberBox').mousedown((function(self) {
        return function(e) { // Start drag
          var x;
          self.areDraggingScrubber = true;
          
          // Move the scrubber bar to the right INITIAL position
          x = scrubHelper(this, e.clientX);
          $(this).find('.sermonScrubberBar').css('left', x);
          self._lastScrubPosition = x / SCRUBBER_WIDTH;
          self.manualUpdate(self._lastScrubPosition);

          $(this).mousemove(function(e) { // Register mover handler
            // Keep moving the scrubber bar to the right position
            x = scrubHelper(this, e.clientX);
            $(this).find('.sermonScrubberBar').css('left', x);
            self._lastScrubPosition = x / SCRUBBER_WIDTH;
            self.updateTimestamp(self._lastScrubPosition);
          });
        };
      })(this)).mouseup((function(self) {
        return function() { // Stop drag by releasing the button
          self.areDraggingScrubber = false;
          self.manualUpdate(self._lastScrubPosition);
          $(this).unbind('mousemove');  // Clear the move handler
        };
      })(this));
      
      return template.get(0);
    },

    newElement: function(type, className, content, appendTo) {
      var e = document.createElement(type);
      if (className != null) { e.className = className; }
      if (typeof content == "string" && content != '') { 
        e.appendChild(document.createTextNode(content)); 
      } else if (content) {
        e.appendChild(content);
      }
      if (appendTo) appendTo.appendChild(e);
      return e;
    },

    filterBy: function(filterField, filterValue) {
      // Check if we are already filtering by this value
      if ([filterField, filterValue] == this.filtering) {
        // No change necessary.
        return;
      }

      // Clear filters
      // If no filter field was given, we will include all sermons.
      var showAll = !filterField && filterField !== 0;
      
      this.sermons = new Array();
      for (var i = 0; this.allSermons[i]; i++) {
        if (showAll || this.allSermons[i][filterField] == filterValue) {
          this.sermons.push(this.allSermons[i]);
        }
      }
      this.filtering = [filterField, filterValue];
      this.pages = this.countPages();
      this.currentPage = 0;
      this.render();
    },

    clearFilter: function() {
      this.filterBy(""); // No filter
    },

    next: function() {
      if (this.currentPage < this.pages - 1) {
        this.currentPage++;
        this.render();
      }
    },

    previous: function() {
      if (this.currentPage > 0) {
        this.currentPage--;
        this.render();
      }
    },
    
    countPages: function() {
      var pages = this.sermons.length / this.sermonsPerPage;
      // assumes non negative result
      return Math.ceil( pages );

    }

  };
// Libraries
// Wanted to keep these inside of the closure to prevent interference
// jQuery UI Core and Accordion follow.

/*
 * jQuery UI 1.7.2
 *
 * 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
 */
jQuery.ui||(function(c){var i=c.fn.remove,d=c.browser.mozilla&&(parseFloat(c.browser.version)<1.9);c.ui={version:"1.7.2",plugin:{add:function(k,l,n){var m=c.ui[k].prototype;for(var j in n){m.plugins[j]=m.plugins[j]||[];m.plugins[j].push([l,n[j]])}},call:function(j,l,k){var n=j.plugins[l];if(!n||!j.element[0].parentNode){return}for(var m=0;m<n.length;m++){if(j.options[n[m][0]]){n[m][1].apply(j.element,k)}}}},contains:function(k,j){return document.compareDocumentPosition?k.compareDocumentPosition(j)&16:k!==j&&k.contains(j)},hasScroll:function(m,k){if(c(m).css("overflow")=="hidden"){return false}var j=(k&&k=="left")?"scrollLeft":"scrollTop",l=false;if(m[j]>0){return true}m[j]=1;l=(m[j]>0);m[j]=0;return l},isOverAxis:function(k,j,l){return(k>j)&&(k<(j+l))},isOver:function(o,k,n,m,j,l){return c.ui.isOverAxis(o,n,j)&&c.ui.isOverAxis(k,m,l)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(d){var f=c.attr,e=c.fn.removeAttr,h="http://www.w3.org/2005/07/aaa",a=/^aria-/,b=/^wairole:/;c.attr=function(k,j,l){var m=l!==undefined;return(j=="role"?(m?f.call(this,k,j,"wairole:"+l):(f.apply(this,arguments)||"").replace(b,"")):(a.test(j)?(m?k.setAttributeNS(h,j.replace(a,"aaa:"),l):f.call(this,k,j.replace(a,"aaa:"))):f.apply(this,arguments)))};c.fn.removeAttr=function(j){return(a.test(j)?this.each(function(){this.removeAttributeNS(h,j.replace(a,""))}):e.call(this,j))}}c.fn.extend({remove:function(){c("*",this).add(this).each(function(){c(this).triggerHandler("remove")});return i.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})},scrollParent:function(){var j;if((c.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){j=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(c.curCSS(this,"position",1))&&(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}else{j=this.parents().filter(function(){return(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!j.length?c(document):j}});c.extend(c.expr[":"],{data:function(l,k,j){return !!c.data(l,j[3])},focusable:function(k){var l=k.nodeName.toLowerCase(),j=c.attr(k,"tabindex");return(/input|select|textarea|button|object/.test(l)?!k.disabled:"a"==l||"area"==l?k.href||!isNaN(j):!isNaN(j))&&!c(k)["area"==l?"parents":"closest"](":hidden").length},tabbable:function(k){var j=c.attr(k,"tabindex");return(isNaN(j)||j>=0)&&c(k).is(":focusable")}});function g(m,n,o,l){function k(q){var p=c[m][n][q]||[];return(typeof p=="string"?p.split(/,?\s+/):p)}var j=k("getter");if(l.length==1&&typeof l[0]=="string"){j=j.concat(k("getterSetter"))}return(c.inArray(o,j)!=-1)}c.widget=function(k,j){var l=k.split(".")[0];k=k.split(".")[1];c.fn[k]=function(p){var n=(typeof p=="string"),o=Array.prototype.slice.call(arguments,1);if(n&&p.substring(0,1)=="_"){return this}if(n&&g(l,k,p,o)){var m=c.data(this[0],k);return(m?m[p].apply(m,o):undefined)}return this.each(function(){var q=c.data(this,k);(!q&&!n&&c.data(this,k,new c[l][k](this,p))._init());(q&&n&&c.isFunction(q[p])&&q[p].apply(q,o))})};c[l]=c[l]||{};c[l][k]=function(o,n){var m=this;this.namespace=l;this.widgetName=k;this.widgetEventPrefix=c[l][k].eventPrefix||k;this.widgetBaseClass=l+"-"+k;this.options=c.extend({},c.widget.defaults,c[l][k].defaults,c.metadata&&c.metadata.get(o)[k],n);this.element=c(o).bind("setData."+k,function(q,p,r){if(q.target==o){return m._setData(p,r)}}).bind("getData."+k,function(q,p){if(q.target==o){return m._getData(p)}}).bind("remove",function(){return m.destroy()})};c[l][k].prototype=c.extend({},c.widget.prototype,j);c[l][k].getterSetter="option"};c.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(l,m){var k=l,j=this;if(typeof l=="string"){if(m===undefined){return this._getData(l)}k={};k[l]=m}c.each(k,function(n,o){j._setData(n,o)})},_getData:function(j){return this.options[j]},_setData:function(j,k){this.options[j]=k;if(j=="disabled"){this.element[k?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",k)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:function(l,m,n){var p=this.options[l],j=(l==this.widgetEventPrefix?l:this.widgetEventPrefix+l);m=c.Event(m);m.type=j;if(m.originalEvent){for(var k=c.event.props.length,o;k;){o=c.event.props[--k];m[o]=m.originalEvent[o]}}this.element.trigger(m,n);return !(c.isFunction(p)&&p.call(this.element[0],m,n)===false||m.isDefaultPrevented())}};c.widget.defaults={disabled:false};c.ui.mouse={_mouseInit:function(){var j=this;this.element.bind("mousedown."+this.widgetName,function(k){return j._mouseDown(k)}).bind("click."+this.widgetName,function(k){if(j._preventClickEvent){j._preventClickEvent=false;k.stopImmediatePropagation();return false}});if(c.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);(c.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(l){l.originalEvent=l.originalEvent||{};if(l.originalEvent.mouseHandled){return}(this._mouseStarted&&this._mouseUp(l));this._mouseDownEvent=l;var k=this,m=(l.which==1),j=(typeof this.options.cancel=="string"?c(l.target).parents().add(l.target).filter(this.options.cancel).length:false);if(!m||j||!this._mouseCapture(l)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){k.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(l)&&this._mouseDelayMet(l)){this._mouseStarted=(this._mouseStart(l)!==false);if(!this._mouseStarted){l.preventDefault();return true}}this._mouseMoveDelegate=function(n){return k._mouseMove(n)};this._mouseUpDelegate=function(n){return k._mouseUp(n)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);(c.browser.safari||l.preventDefault());l.originalEvent.mouseHandled=true;return true},_mouseMove:function(j){if(c.browser.msie&&!j.button){return this._mouseUp(j)}if(this._mouseStarted){this._mouseDrag(j);return j.preventDefault()}if(this._mouseDistanceMet(j)&&this._mouseDelayMet(j)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,j)!==false);(this._mouseStarted?this._mouseDrag(j):this._mouseUp(j))}return !this._mouseStarted},_mouseUp:function(j){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=(j.target==this._mouseDownEvent.target);this._mouseStop(j)}return false},_mouseDistanceMet:function(j){return(Math.max(Math.abs(this._mouseDownEvent.pageX-j.pageX),Math.abs(this._mouseDownEvent.pageY-j.pageY))>=this.options.distance)},_mouseDelayMet:function(j){return this.mouseDelayMet},_mouseStart:function(j){},_mouseDrag:function(j){},_mouseStop:function(j){},_mouseCapture:function(j){return true}};c.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);;
/*
 * jQuery UI Accordion 1.7.2
 *
 * 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
 */
/*
 * jQuery UI Accordion 1.7.2
 *
 * 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.2",
	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);



