/*	Script: multiple.open.accordion.js
		Creates a Mootools <Fx.Accordion> that allows the user to open more than one element.
		
		Dependancies:
			 mootools - 	<Moo.js>, <Function.js>, <Array.js>, <String.js>, <Element.js>, <Fx.Base.js>, <Fx.Elements.js>, <Fx.Styles.js>, <Fx.Style.js>
			
		Author:
			Aaron Newton, <aaron [dot] newton [at] cnet [dot] com>

		
		Class: MultipleOpenAccordion
		Extends the <Fx.Elements> class from Mootools for an accordion element that allows
		the user to open more than one element.
		
		Arguments:
		togglers - elements that activate each section
		elements - the elements to resize
		options - the options object of key/value settings
		
		Options:
		openAll - (boolean) open all elements on startup; defaults to true.
		allowMultipleOpen - (boolean) allows users to open more than one element at a time; defaults to true.
		firstElementsOpen - (array) an array of elements to open on startup;
				only used if openAll = false and allowMultipleOpen = true;
				defaults to [0]; can be empty ([]) to signifiy that all should be closed;
		start - (string) 'first-open' slides open each element in firstElementsOpen;
										 'open-first' opens each element in firstElementsOpen immediately using no effects (default)
		fixedHeight - integer, if you want your accordion to have a fixed height. defaults to false.
		fixedWidth - integer, if you want your accordion to have a fixed width. defaults to false.
		alwaysHide - boolean, if you want the ability to close your only-open item. defaults to true.
		wait - boolean. means that open and close transitions can cancel current ones (so if you click
		 on items before the previous finishes transitioning, the clicked transition will fire canceling the previous). 
		 true means that if one element is sliding open or closed, clicking on another will have no effect. 
		 for Accordion defaults to false.
		onActive - function to execute when an element starts to show; passed arguments: (toggler, section)
		onBackground - function to execute when an element starts to hide; passed arguments: (toggler, section)
		height - boolean, will add a height transition to the accordion if true. defaults to true.
		opacity - boolean, will add an opacity transition to the accordion if true. defaults to true.
		width - boolean, will add a width transition to the accordion if true. defaults to false, 
						css mastery is required to make this work!
	*/
var MultipleOpenAccordion = Fx.Elements.extend({
	options: {
		openAll: true,
		allowMultipleOpen: true,
		firstElementsOpen: [0],
		start: 'open-first',
		fixedHeight: false,
		fixedWidth: false,
		alwaysHide: true,
		wait: false,
		onActive: Class.empty,
		onBackground: Class.empty,
		height: true,
		opacity: true,
		width: false
	},
	initialize: function(togglers, elements, options){
		this.parent(elements, options);
		this.setOptions(options);
		this.previousClick = null;
		this.elementsVisible = [];
		togglers.each(function(tog, i){
			$(tog).addEvent('click', function(){this.toggleSection(i)}.bind(this));
		}, this);
		this.togglers = togglers;
		this.h = {}; 
		this.w = {};
		this.o = {};
		this.now = [];
		this.elements.each(function(el, i){
			el = $(el);
			this.now[i] = {};
			el.setStyle('overflow','hidden');
			if(!(this.options.openAll && this.options.allowMultipleOpen)) el.setStyle('height', 0);
		}, this);
		if(!this.options.openAll || !this.options.allowMultipleOpen) {
			switch(this.options.start){
				case 'first-open': this.showSection(this.options.firstElementsOpen[0]); break;
				case 'open-first': this.toggleSection(this.options.firstElementsOpen[0]); break;
			}
		}
		if (this.options.openAll && this.options.allowMultipleOpen) this.showAll();
		else if (this.options.allowMultipleOpen) this.openSections(this.options.firstElementsOpen);
	},
	hideThis: function(i){ //sets up the effects for hiding an element
		this.elementsVisible[i] = false;
		if (this.options.height) this.h = {'height': [this.elements[i].offsetHeight, 0]};
		if (this.options.width) this.w = {'width': [this.elements[i].offsetWidth, 0]};
		if (this.options.opacity) this.o = {'opacity': [this.now[i]['opacity'] || 1, 0]};
		this.fireEvent("onBackground", [this.togglers[i], this.elements[i]]);
	},

	showThis: function(i){ //sets up the effects for showing an element
		this.elementsVisible[i] = true;
		if (this.options.height) this.h = {'height': [this.elements[i].offsetHeight, this.options.fixedHeight || this.elements[i].scrollHeight]};
		if (this.options.width) this.w = {'width': [this.elements[i].offsetWidth, this.options.fixedWidth || this.elements[i].scrollWidth]};
		if (this.options.opacity) this.o = {'opacity': [this.now[i]['opacity'] || 0, 1]};
		this.fireEvent("onActive", [this.togglers[i], this.elements[i]]);
	},
/*	Property: toggleSection
		Opens or closes a section depending on its state and the options of the Accordion.
		
		Argumetns:
		iToToggle - (integer) the index of the section to open or close
	*/
	toggleSection: function(iToToggle){
		//let's open an object, or close it, depending on it's state
		//now, if the index to toggle isn't the previous click
		//or we're going to allow items to be closed (so that all of them are closed
		//or we're allowing more than one item to be open at a time, continue
		//otherwise, we're looking at an item that was just clicked, and it should already be open
		if(iToToggle != this.previousClick || this.options.alwaysHide || this.options.allowMultipleOpen) {
			//save the previous click
			this.previousClick = iToToggle;
			var objObjs = {};
			var err = false;
			//go through each element
			this.elements.each(function(el, i){
				var update = false;
				//set up it's now state
				this.now[i] = this.now[i] || {};
				//if the element is the one clicked
				if(i==iToToggle){
					//if the element is visible, hide it if we allow alwaysHide or multiple
					if (this.elementsVisible[i] && (this.options.allowMultipleOpen || this.options.alwaysHide)){
						//if ! wait and timer
						if(!(this.options.wait && this.timer)) {
							//hide it
							update = true;
							this.hideThis(i);
						} else {
							this.previousClick = null;
							err = true;
						}
					} else if(!this.elementsVisible[i]){
					//else if hidden, show it
						//if ! wait and timer
						if(!(this.options.wait && this.timer)) {
							//show it
							update = true;
							this.showThis(i);
						} else {
							this.previousClick = null;
							err = true;
						}
					}
				} else if(this.elementsVisible[i] && !this.options.allowMultipleOpen) {
				//else (not clicked) if it's visible, hide it, unless we allow multiple open
					//if ! wait and timer
					if(!(this.options.wait && this.timer)) {
						//hide it
						update = true;
						this.hideThis(i);
					} else {
						this.previousClick = null;
						err = true;
					}
				} //else it's not clicked, it's not open, so leave it alone because we allow multiples
				//set up the effect instructions
				if(update) objObjs[i] = $merge(this.h, $merge(this.o, this.w));
			}, this);
			//if there's an error, just stop
			if (err) return false;
			//execute the custom function, which resizes everything.
			return this.custom(objObjs);
		}
		return false;
	},
/*	Property: showSection
		Opens a section of the accordion if it's not open already.
		
		Arguments:
		i - (integer) the index of the section to show
		useFx - (boolean) open it immediately (false) or slide it open using the effects (true);  defaults to false;
	*/
	showSection: function(i, useFx){
		if($pick(useFx, false)) {
			if (!this.elementsVisible[i]) this.toggleSection(i);
		} else {
			this.setSectionStyle(i,$(this.elements[i]).scrollWidth, $(this.elements[i]).scrollHeight, 1);
			this.elementsVisible[i] = true;
			this.fireEvent("onActive", [this.togglers[i], this.elements[i]]);
		}
	},
/*	Property: hideSection
		Closes a section of the accordion if it's not closed already.
		
		Arguments:
		i - (integer) the index of the section to hide
		useFx - (boolean) close it immediately (false) or slide it closed using the effects (true);  defaults to false;
	*/
	hideSection: function(i, useFx){
		if($pick(useFx, false)) {	
			if (this.elementsVisible[i]) this.toggleSection(i);
		} else {
			this.setSectionStyle(i,0,0,0);
			this.elementsVisible[i] = false;
			this.fireEvent("onBackground", [this.togglers[i], this.elements[i]]);
		}
	},
	//internal function; sets a section (i) to the width (w), height (h), and opacity (o) passed in
	setSectionStyle: function(i,w,h,o){ 
			if (this.options.opacity) $(this.elements[i]).setOpacity(o);
			if (this.options.height) $(this.elements[i]).setStyle('height',h+'px');
			if (this.options.width) $(this.elements[i]).setStyle('width',w+'px');
	},
/*	Property: showAll
		Opens all the elements in the accordion immediately; used on startup	*/
	showAll: function(){
		if(this.options.allowMultipleOpen){
			this.elements.each(function(el,idx){
					this.showSection(idx, false);
			}, this);
		}
	},
/*	Property: hideAll
		Closes all the elements in the accordion immediately; used on startup	*/
	hideAll: function(){
		if(this.options.allowMultipleOpen){
			this.elements.each(function(el,idx){
				this.hideSection(idx, false);
			}, this);
		}
	},
/*	Property: openSection
		Opens specific sections of the accordion immediately; used on startup.
		
		Arguments:
		sections - array of indexes to open.
	*/
	openSections: function(sections) {
		if(this.options.allowMultipleOpen){
			this.elements.each(function(el,idx){
				if(sections.test(idx)) this.showSection(idx, false);
				else this.hideSection(idx, false);
			}, this);
		}
	}
});
MultipleOpenAccordion.implement(new Options);
MultipleOpenAccordion.implement(new Events);
/* do not edit below this line */   

/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/common/layout.widgets/multiple.open.accordion.js,v $
$Log: multiple.open.accordion.js,v $
Revision 1.8  2007/06/21 20:20:29  newtona
multiopenaccordion showall and hideall weren't working; closing bug 296095

Revision 1.7  2007/05/29 20:34:34  newtona
refactored a lot; fixed issues with onBackground and onActive events.

Revision 1.6  2007/04/04 17:28:53  newtona
subtle syntax error fix.

Revision 1.5  2007/03/08 23:29:59  newtona
strict javascript warnings cleaned up

Revision 1.4  2007/02/27 21:46:43  newtona
docs update; fixing references

Revision 1.3  2007/02/07 20:51:55  newtona
implemented Options class
implemented Events class

Revision 1.2  2007/01/26 05:53:47  newtona
syntax update for mootools 1.0

Revision 1.1  2007/01/22 21:59:03  newtona
moved from fx.multiple.open.accordion.js

Revision 1.1  2007/01/09 02:39:35  newtona
renamed addons directory to "common" directory

Revision 1.5  2006/12/06 20:14:59  newtona
carousel - improved performance, changed some syntax, actually deployed into usage and tested
cnet.nav.accordion - improved css selectors for time
multiple accordion - fixed a typo
dbug.js - added load timers
element.cnet.js - changed syntax to utilize mootools more effectively
function.cnet.js - equated $set to $pick in preparation for mootools v1

Revision 1.4  2006/11/06 19:19:31  newtona
fixed a bug and removed some dbug.log statements

Revision 1.3  2006/11/04 01:35:27  newtona
removing a dbug line

Revision 1.2  2006/11/04 00:53:45  newtona
no change

Revision 1.1  2006/11/02 21:28:08  newtona
checking in for the first time.


*/
