/* 
	Expanding/Shrinking Boxes
	(c) Jason M. Knight 2007
	Version 1.2 - 1 December 2007
	
	Public domain - please at least leave the three above lines intact.
	None of that open source horseshit allowed in here. If you are going
	to give something away, for **** sake JUST GIVE IT AWAY!!!
*/

// this divides the height to determine how fast to expanding smaller = faster
var boxMoveDivider=8; 

// and this sets the lowest number of pixels to move  - do NOT set to 0 or 
// your section won't close all the way
var boxMoveMinimum=4;

// how often to run our timer - 30ms works good 
var boxInterval=30;

/* 
	IE's timer is 10ms 'slower' than everyone else. The following conditional
	corrects for that
@cc_on
	@if (@_jscript_version >= 4)
		boxInterval-=10;
	@end
@*/

// internal use - user should not set these 
var boxArray=new Array();
var boxTimerActive=0;

/*
	The boxTimer function handles resizing all expanding/shrinking boxes. It
	is called by the toggleAccordion class to start it running, and only runs
	as long as something is being resized.
*/

function boxTimer() {
	boxTimerActive=0;
	for (t=0; t<boxArray.length; t++) {
		if (boxArray[t].className.indexOf('s_expanding')!=-1) {

			// calculate new height based on current
			total=boxArray[t].offsetHeight + (
				Math.floor(boxArray[t].offsetHeight/boxMoveDivider) +
				boxMoveMinimum
			);

			// check to see if we go past where we want
			if (total>=boxArray[t].scrollHeight) {

				// when past it, so set it to our target
				total=boxArray[t].scrollHeight;

				// switch the class to say it's fully expanded 
				boxArray[t].className=boxArray[t].className.replace(/s_expanding/,'s_expanded');

				// and switch the class on the control as well 
				control=document.getElementById(boxArray[t].id+'_sControl');
				control.className=control.className.replace(/sc_expanding/,'sc_expanded');

			// otherwise keep it going
			} else boxTimerActive++; 

			// then finally set the new height
			boxArray[t].style.height=total+'px';

		} else if (boxArray[t].className.indexOf('s_shrinking')!=-1) {

			// calculate new height for shrinking
			total=boxArray[t].offsetHeight - (
				Math.floor(boxArray[t].offsetHeight/boxMoveDivider) +
				boxMoveMinimum
			);

			if (total<=0) {

				// went below where we wanted so set that
				total=0;

				// switch the class to say it's fully shrunk
				boxArray[t].className=boxArray[t].className.replace(/s_shrinking/,'s_shrunk');

				// do the same to the control
				control=document.getElementById(boxArray[t].id+'_sControl');
				control.className=control.className.replace(/sc_shrinking/,'sc_shrunk');

			// otherwise keep it going
			} else boxTimerActive++;

			// then finally set the new height
			boxArray[t].style.height=total+'px';

		}	
	}

	// if anything is still updating we need to go again 
	if (boxTimerActive>0) setTimeout(boxTimer,boxInterval); 
}

/*
	checkRadio is called by toggleAccordion to first see if an item
	is part of a radio set. If it is, all other items in that radio set
	are set to shrinking
*/

function checkRadio(target) {
	// see if it's a s_radio
	radioNumber=target.className.match(/\bs_radio\w+\b/);
	if (radioNumber) {
		// if so cycle through our known boxArray items
		for (t=0; t<boxArray.length; t++) {
			cName=boxArray[t].className;
			// We do NOT want to effect the one we're actually working on
			if ((boxArray[t]!=target) && (cName.indexOf(radioNumber)!=-1)) {
				control=document.getElementById(boxArray[t].id+'_sControl');
				if (cName.indexOf('s_expanded')!=-1) {
					boxArray[t].className=cName.replace(/s_expanded/,'s_shrinking');
					control.className=control.className.replace(/sc_expanded/,'sc_shrinking');
				} else if (cName.indexOf('s_expanding')!=-1) {
					boxArray[t].className=cName.replace(/s_expanding/,'s_shrinking');
					control.className=control.className.replace(/sc_expanding/,'sc_shrinking');
				}
			}
		}
	}
}

/*
	toggleAccordion is called by the anchors we add to our controls. It swaps
	the class on our target to mark it for resizing, then checks to see if our
	boxTimer is running - if not, we start it up.
*/

function toggleAccordion(sender,targetID) {

	// the blur removes the outline from the click - makes it purty
	sender.blur();

	// grab the actual target element, and the control wrapper
	target=document.getElementById(targetID);
	control=sender.parentNode;
	cName=target.className;
	
	// With radio classes we need to set all of it's kin to shrink
	
	// next we check the targets class, and switch it as appropriate 
	// using match and replace means we preserve other classes that might
	// be in there.
	
	if (cName.indexOf('s_shrunk')!=-1) {
		target.className=cName.replace(/s_shrunk/,'s_expanding');
		control.className=control.className.replace(/sc_shrunk/,'sc_expanding');
		checkRadio(target);
	} else if (cName.indexOf('s_expanded')!=-1) {
		target.className=cName.replace(/s_expanded/,'s_shrinking');
		control.className=control.className.replace(/sc_expanded/,'sc_shrinking');
	} else if (cName.indexOf('s_shrinking')!=-1) {
		target.className=cName.replace(/s_shrinking/,'s_expanding');
		control.className=control.className.replace(/sc_shrinking/,'sc_expanding');
		checkRadio(target);
	} else if (cName.indexOf('s_expanding')!=-1) {
		target.className=cName.replace(/s_expanding/,'s_shrinking');
		control.className=control.className.replace(/sc_expanding/,'sc_shrinking');
	}
	
	// and if the timer isn't running, get it going 
	if (boxTimerActive<=0) boxTimer();
}

/*
	The setupAccordions function is what actually does the gruntwork of parsing 
	the HTML to make the controls work. It should be called either via an onload
	property or directly in the HTML right before the close of the BODY. I prefer
	the latter method because then the rendering of the page is 'suspended' until
	the javascript and CSS are loaded, meaning the content doesn't 'jump' across
	the page as changes are made. (as much)
*/

function setupAccordions() {
	var tList=document.getElementsByTagName('*');
	for (t=0; t<tList.length; t++) {

		// We need to set up all of our control elements. By using a standard
		// suffix we can nab them by their ID
		if (tList[t].id.indexOf('_sControl')!=-1) {

			// since we have a control, it must have a target - by stripping the
			// suffix we have an easy way to find it.
			targetID=tList[t].id.replace(/_sControl/,'');
			target=document.getElementById(targetID);

			// Add it to our list that will be checked by the timer 
			
			boxArray[boxArray.length]=target;	

			// turn our control into an actual control - in the future this may also
			// detect if it's already and anchor and modify that directly... 
			tList[t].innerHTML='<a href="javascript:void(0);" onclick="toggleAccordion(this,\''+targetID+'\'); return false;">'+tList[t].innerHTML+'<span></span></a>';

			// initialize shrinking/shrunk 
			if ((target.className.indexOf('s_shrinking')!=-1) || (tList[t].className.indexOf('s_shrunk')!=-1)) {
				target.style.height='0px';

				// if set to 'shrinking' it should be 'shrunk' - this is to trap 
				// borders and padding if used via CSS
				target.className=target.className.replace(/s_shrinking/g,'s_shrunk');

				// while we're in here, set up the state of our control 
				state='sc_shrunk';

			// otherwise it's open, so mark the control as such
			} else state='sc_expanded'; 

			// Then set the control to the control class, and set it's state.
			tList[t].className+=' s_control '+state;

		}	
	}
}

