;(function($) {

$.fn.extend({
	accordion: function(settings, value) {
		return typeof settings == "string" ?
			this.trigger(settings + ".ui-accordion", [value]) :
			this.each(function() {
				$.data(this, "ui-accordion") || $.data(this, "ui-accordion", new $.ui.accordion(this, settings));
			});
	},
	// deprecated, use .accordion()
	makeAccordion: function() {
		return this.accordion.apply( this, arguments );
	},
	// deprecated, use .accordion("activate", index)
	changeAccordion: function(key, value) {
		return this.trigger(key + ".ui-accordion", [value]);
	},
	// deprecated, use .accordion("enable")
	enableAccordion: function() {
		return this.trigger("enable.ui-accordion");
	},
	// deprecated, use .accordion("disable")
	disableAccordion: function() {
		return this.trigger("disable.ui-accordion");
	},
	// deprecated, use .accordion("remove")
	removeAccordion: function() {
		return this.trigger("remove.ui-accordion");
	},
	// deprecated, use .accordion("activate", index)
	activate: function(index) {
		return this.changeAccordion("activate", index);
	},
	// deprecated, use .accordion("remove")
	unaccordion: function() {
		return this.removeAccordion.apply(this, arguments);
	}
});

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

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

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

	settings.headers
		.not(settings.active || "")
		.next()
		.hide();
	settings.active.parent().andSelf().addClass(settings.selectedClass);
	
	$(container)
	.bind((settings.event || "") + ".ui-accordion", clickHandler)
	.bind("activate.ui-accordion", activateHandler)
	.bind("enable.ui-accordion", function() {
		$.data(this, "ui-accordion").settings.disabled = false;
	})
	.bind("disable.ui-accordion", function() {
		$.data(this, "ui-accordion").settings.disabled = true;
	})
	.one("remove.ui-accordion", function() {
		var settings = $.data(this, "ui-accordion").settings;
		$(this)
		.unbind( settings.event + ".ui-accordion" )
		.unbind( "activate.ui-accordion" )
		.unbind( "enable.ui-accordion" )
		.unbind( "disable.ui-accordion" );
		settings.headers.next().css("display", "");
		if ( settings.fillSpace || settings.autoheight ) {
			settings.headers.next().css("height", "");
		}
		$.removeData(this, "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 settings = $.data(this, "ui-accordion").settings;
	settings.running = cancel ? 0 : --settings.running;
	if ( settings.running )
		return;
	if ( settings.clearStyle ) {
		settings.toShow.add(settings.toHide).css({
			height: "",
			overflow: ""
		});
	}
	$(this).trigger("changed.ui-accordion", settings.data);
}

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

function clickHandler(event) {
	var settings = $.data(this, "ui-accordion").settings;
	if (settings.disabled)
		return false;
	
	// called only when using activate(false) to close all parts programmatically
	if ( !event.target && !settings.alwaysOpen ) {
		settings.active.parent().andSelf().toggleClass(settings.selectedClass);
		var toHide = settings.active.next();
		var toShow = settings.active = $([]);
		toggle.call(this, toShow, toHide );
		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(settings.header).length )
		while ( !clicked.is(settings.header) )
			clicked = clicked.parent();
	
	var clickedActive = clicked[0] == settings.active[0];
	
	// if animations are still active, or the active header is the target, ignore click
	if (settings.running || (settings.alwaysOpen && clickedActive))
		return false;
	if (!clicked.is(settings.header))
		return;

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

	// find elements to show and hide
	var toShow = clicked.next(),
		toHide = settings.active.next(),
		data = [clicked, settings.active, toShow, toHide],
		down = settings.headers.index( settings.active[0] ) > settings.headers.index( clicked[0] );
	
	settings.active = clickedActive ? $([]) : clicked;
	toggle.call(this, toShow, toHide, data, clickedActive, down );

	return false;
};

function activateHandler(event, index) {
	// IE manages to call activateHandler on normal clicks
	if ( arguments.length == 1 )
		return;
	// call clickHandler with custom event
	clickHandler.call(this, {
		target: findActive( $.data(this, "ui-accordion").settings.headers, index )[0]
	});
};

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(settings, additions) {
			settings = $.extend({
				easing: "swing",
				duration: 300
			}, settings, additions);
			if ( !settings.toHide.size() ) {
				settings.toShow.animate({height: "show"}, settings);
				return;
			}
			var hideHeight = settings.toHide.height(),
				showHeight = settings.toShow.height(),
				difference = showHeight / hideHeight;
			settings.toShow.css({ height: 0, overflow: 'hidden' }).show();
			settings.toHide.filter(":hidden").each(settings.complete).end().filter(":visible").animate({height:"hide"},{
				step: function(now) {
					var current = (hideHeight - now) * difference;
					if ($.browser.msie || $.browser.opera) {
						current = Math.ceil(current);
					}
					settings.toShow.height( current );
				},
				duration: settings.duration,
				easing: settings.easing,
				complete: function() {
					if ( !settings.autoheight ) {
						settings.toShow.css("height", "auto");
					}
					settings.complete();
				}
			});
		},
		bounceslide: function(settings) {
			this.slide(settings, {
				easing: settings.down ? "bounceout" : "swing",
				duration: settings.down ? 1000 : 200
			});
		},
		easeslide: function(settings) {
			this.slide(settings, {
				easing: "easeinout",
				duration: 700
			})
		}
	}
});

})(jQuery);
