/**
 * @author luke.cuthbertson <luke.cuthbertson@heathwallace.com>
 * @projectDescription jQuery extension for the implementation of accordions (hide/shows)
 * Complete rebuild of accordion extension from 1.x. Aims to provide:
 * better performance - zippier feel even with large accordions 
 * more eventing and callback control, 
 * built in support for radios and checkboxes as triggers, 
 * built in support for 'Hide All', 'Show All' and 'Toggle All' links,
 * supports both parent and sibling based trigger/content relationships, so it can be used with <dl>s and <table>s
 * 
 * @version 2.3 beta
 * @date 02/02/2010
 */
(function($){
	
	$.fn.accordion = function(opts){
		
		var opts = $.extend({
			trigger: 'a.triggerLink', //selector
			content: 'div.showHideContent', //selector
			toggleAll: 'a.toggleAllLink', //selector
			showAll: 'a.showAllLink', //selector
			hideAll: 'a.hideAllLink', //selector
			node: null, //selector or null to make trigger the branch point
			open: 'open', //CSSClass
			time: 300, //milliseconds
			forceClose: false, //should opening another close the open one
			closing: function(){}, //callback
			closed: function(){}, //callback
			opening: function(){}, //callback
			opened: function(){}, //callback
			text: null,//{closed: 'show', open: 'hide'}, //text for triggers
			allText: {closed: 'show all', open: 'hide all'} //text for '**** All' links
		}, opts);
		
		var $wrapper = this;
	
		function set(state, block){
			if(state == 'closed'){
				block.$content.css({
					display: 'none',
					height: 0
				});
				block.isOpen = false; 
				if (!block.isOptionButton && opts.text) {
					block.$link.html(opts.text.closed);
				}
				return;
			}
			if (state == 'open') {
				block.$content.css({
					display: 'block',
					height: 'auto'
				});
				block.isOpen = true;
				if (!block.isOptionButton && opts.text) {
					block.$link.html(opts.text.open);
				}
			}			
		}
			
		function open(block){
			opts.opening(block);
			//block.$link.trigger('accordion.onopening', block)
			$wrapper.trigger('accordion.onopening', block)
			if(opts.text){
				block.$link.html(opts.text.open);
			}
			block.$link.closest(opts.node || opts.trigger).addClass(opts.open);
			block.$content.addClass(opts.open);
			block.isOpen = true;
			var h = block.$content.css('display', 'block').height();
			var _h = block.$content.css('height', 'auto').height();
			block.$content.css('height', h);
			block.$content.addClass(opts.open).stop().animate({height: _h}, opts.time, function(){
				block.$content.css({
					height: 'auto',
					overflow: 'visible'
				});
				//block.$link.trigger('accordion.onopen', block);
				$wrapper.trigger('accordion.onopen', block);
				opts.opened(block);
			});		
		}
		
		function close(block){
			if(block.isOptionButton){
				block.$link.attr('checked', false);
			}			
			block.isOpen = false;
			block.$content.removeClass(opts.open).stop().slideUp(opts.time, function(){
				block.$link.closest(opts.node || opts.trigger).removeClass(opts.open);
				block.$content.removeClass(opts.open);
				block.$content.css({
					height: 0
				});
				//block.$link.trigger('accordion.onclose', block);
				$wrapper.trigger('accordion.onclose', block);
				opts.closed(block);				
			});
			opts.closing(block);	
			//block.$link.trigger('accordion.onclosing', block);
			$wrapper.trigger('accordion.onclosing', block);
			if(opts.text){
				block.$link.html(opts.text.closed);
			}	
		}
		
		this.each(function(){
		
			var accordion = $(this).css('zoom', 1);
			var blocks = [];

			function checkOthers(block){
				for (var i = 0; i < blocks.length; i++) {
					if (blocks[i].$content.index(block.$content[0]) != -1 && blocks[i] != block) {
						blocks[i].isOpen = (block.isOpen) ? true : false;
						blocks[i].$link.html((block.isOpen) ? opts.text.open : opts.text.closed);
					}
				}
			}			

			function clickHandler(block){
				
				if (block.isOpen) {
					close(block);
					checkOthers(block);
				}
				else{
					if(opts.forceClose){
						closeAll(block, function(){
							open(block);
							checkOthers(block);
						});
						return;
					}
					open(block);
					checkOthers(block);					
				}
			}

			function closeAll(ignore, callback){
				var someOpen = false;
				for(var i = 0; i < blocks.length; i++){
					if((ignore && ignore != blocks[i] || !ignore) && blocks[i].isOpen){
						someOpen = someOpen || blocks[i].isOpen;
						close(blocks[i]);
					}
				}
				if(callback){
					setTimeout(function(){
						callback();
					}, (someOpen) ? opts.time : 0);
				}
			}
			
			function openAll(){
				for(var i = 0; i < blocks.length; i++){
					open(blocks[i]);
				}
			}
			
			var triggers = accordion.find(opts.trigger).each(function(){
				
				var block = {};
				
				block.$link = $(this);
				var $branchPoint = (opts.node) ? block.$link.closest(opts.node) : block.$link;
				block.$content = $branchPoint.find(opts.content); //if node based search we will find conent
				if(!block.$content.size()){
					//otherwise take a look at the next sibling or inside the next sibling
					block.$content = ($branchPoint.next().is(opts.content)) ? $branchPoint.next() : $branchPoint.next().find(opts.content)
				}
				
				var _position = (block.$content.css('position') == 'absolute') ? 'absolute' : 'relative';
				block.$content.css({
					position: _position,
					zoom: 1,
					overflow: 'hidden'
				});
				
				block.isOpen = true;
				
				var tagName = block.$link.attr('tagName').toLowerCase();
				block.isOptionButton = (tagName == 'input' && (block.$link.attr('type') == 'radio' || block.$link.attr('type') == 'checkbox')) ? true : false;
				if(block.isOptionButton && this.checked){
					block.$content.addClass(opts.open);
				}
				
				if(!block.$content.hasClass(opts.open)){
					set('closed', block);
				}
				
				block.$link.data('content', block);
				blocks.push(block);
				
				block.$link.click(function(e){
					if (!block.isOptionButton) {
						e.preventDefault();
					}
					clickHandler(block);
				})
				.bind('accordion.close', function(){
					close(block);
				})
				.bind('accordion.open', function(){
					open(block);
				})
				.bind('accordion.set', function(e, state){
					set(state, block);
				});
				
			});

			accordion.find(opts.hideAll).click(function(e){
				e.preventDefault();
				closeAll();
				toggleAllLinks.html(opts.allText.closed)
			});

			accordion.find(opts.showAll).click(function(e){
				e.preventDefault();
				openAll();
				toggleAllLinks.html(opts.allText.open)
			});
			
			var toggleAllLinks = accordion.find(opts.toggleAll).click(function(e){
				e.preventDefault();
				var openCount = 0;
				for(var i = 0; i < blocks.length; i++){
					if(blocks[i].isOpen){
						openCount++;
					}
				}
				if(openCount != blocks.length){
					openAll();
					$(this).html(opts.allText.open)
					return;					
				}
				closeAll();
				$(this).html(opts.allText.closed);								
			});
			
		});
		
		return this;
	}
	
})(jQuery);

