function interp2(start, stop, fraction)
{
	var t = fraction * fraction * (3 - 2*fraction);
	return start + (stop - start) * t;
}

function interp(start, stop, fraction)
{
	return interp2(start, stop, fraction);
}

function get_real_height(element)
{
	if (element.is(':visible')) {
		return element.height();
	} else {
		element.show();
		var old_height = element.height();

		element.height('auto');
		var height = element.height();
		element.height(old_height);

		element.hide();
		return height;
	}
}

/* this function is a huge catch-all mess, should be factored */		
function animate_accordion(clicked_link)
{
	// triples of [element, starting_height, ending_height]
	var animations = [];

	var clicked_accordion = clicked_link.closest('li').children('ul');
	var clicked_accordion_height = get_real_height(clicked_accordion);
	if (clicked_accordion.is(':visible')) {
		animations.push([clicked_accordion, clicked_accordion_height, 0]);
		clicked_link.removeClass('expanded');
	} else {
		animations.push([clicked_accordion, 0, clicked_accordion_height]);
		clicked_link.addClass('expanded');
	}

	clicked_accordion.parent().siblings().children('ul')
		.each(function() {
			var element = $(this);
			if (element.is(':visible')) {
				element.siblings('a').removeClass('expanded');
				animations.push([element, get_real_height(element), 0]);
			}
		});

	var animation_length = 300;
	var start_time = new Date().getTime();

	var id = setInterval(
		function () {
			var current_time = new Date().getTime();
			var elapsed_time = current_time - start_time;
			var fraction = elapsed_time / animation_length
			if (fraction > 1.0) {
				clearInterval(id);
				fraction = 1.0;
			}
			for (var i = 0; i < animations.length; ++i) {
				var animation = animations[i];
				var height = interp(animation[1], animation[2], fraction);
				if (height == 0) {
					animation[0].hide();
					animation[0].height('auto');
				} else {
					// hack around IE's broken height reporting
					if (fraction == 1.0)
						height = 'auto';

					animation[0].show();
					animation[0].height(height);
				}
			}
		},
		1
	);
	return id;
}

function cancel_animation(id)
{
	clearInterval(id);
}

$(document).ready(
	function() {
		var animation_id;
		$("ul.accordion>li>ul").hide();
		$("ul.accordion>li>:not(ul)").click(function(event) {
			cancel_animation(animation_id);
			animation_id = animate_accordion($(event.target));
			event.preventDefault();
		});
	}
);
