﻿var Accordion = Class.create({
	version: "1.0",
	initialize: function(options){
		// option defaults
		this.options = Object.extend({
			containerId: "accordion",
			tabSelector: "div#accordion div.tab",
			triggerSelector: "div#accordion div.trigger",
			drawerSelector: "div#accordion div.drawer",
			eventType: "click",
			defaultOpenTabs: [0] // can alternatively add an 'open' class to the tab element
		}, options || {});
		
		// accordion parts
		this.container = $(this.options.containerId);
		this.tabs = $$(this.options.tabSelector);
		this.triggers = $$(this.options.triggerSelector);
		this.drawers = $$(this.options.drawerSelector);
		this.accordionId = this.options.containerId;
		if (this.options.animator){
			this.animator = this.options.animator(this);
		}
		
		// set the state of all tabs to open/closed based on this.options.defaultOpenTabs or if there's an existing 'open' css class
		var openedTabs = [], closedTabs = [];
		this.tabs.each(function(tab, index){
			var bOpen = this.options.defaultOpenTabs.member(index) || tab.hasClassName('open');
			if(bOpen){
			    // set to open
		       tab.removeClassName("closed").addClassName("open");
		       tab._open = true;
		       openedTabs.push(tab);
		     } else {
			    // set to closed
			    tab.removeClassName("open").addClassName("closed");
			    tab._open = false;
			    closedTabs.push(tab);
			}
		}.bind(this));
		
		this.container.fire(this.accordionId+"_accordion:initialized",{inactiveTabs: closedTabs, activeTabs: openedTabs});
		
		// check query string for default tab
		//this.readQueryString();
				
		// quick close all tabs except default (no parallel event fired, therefore no animation)
		//this.closeOtherTabs(this.tabs[this.options.defaultOpenTab], "quick");
		//defaultOpenTab: 0
	},
	/*
	readQueryString: function(){
		var originalDefaultOpenTab = this.options.defaultOpenTab;
		this.options.defaultOpenTab = window.location.search.toQueryParams()[this.accordionId];
		if(typeof(this.tabs[this.options.defaultOpenTab]) == "undefined"){
			this.options.defaultOpenTab	= originalDefaultOpenTab;
		}
	},
	*/
	openTab: function(tab){
		// allow tab to be an element, index, or id
		if(!(Object.isElement(tab))){
			tab = this.getTab(tab);
		}
		
		if(tab._open) { return; }
		tab.removeClassName("closed").addClassName("open");
		tab._open = true;
		tab.fire(this.accordionId+"_tab:opened");
	},
	closeTab: function(tab){
		// allow tab to be an index
		if(!(Object.isElement(tab))){
			tab = this.getTab(tab);
		}
		if(!tab._open) { return; }
		tab.removeClassName("open").addClassName("closed");
		tab._open = false;
		tab.fire(this.accordionId+"_tab:closed");
	},
	closeOtherTabs: function(tabToOpen, type){
		// find all open tabs
		var currentlyOpenTabs = this.tabs.findAll(function(currentTab){
			return currentTab._open; 
		});
		
		// close each open tab
		currentlyOpenTabs.each(function(currentTab){
			currentTab.removeClassName("open").addClassName("closed");
			currentTab._open = false;
		});
		
		// set active tab to open
		tabToOpen.removeClassName("closed").addClassName("open");
		tabToOpen._open = true;
		
		// if type is quick, fire initialization event
		if (type == "quick"){
			this.container.fire(this.accordionId+"_accordion:initialized",{inactiveTabs: currentlyOpenTabs, activeTab: tabToOpen});
		// if type is not quick, fire parallel event
		} else {
			tabToOpen.fire(this.accordionId+"_tab:parallel", {tabToClose: currentlyOpenTabs});
		}
	},
	enableTab: function(tab){
		return this.getTab(tab).removeClassName('disabled');
	},
	
	disableTab: function(tab){
		return this.getTab(tab).addClassName('disabled');
	},
	
	isTabDisabled: function(tab){
		return this.getTab(tab).hasClassName('disabled');
	},
		
	
	isTabOpen: function(tab){
		if(Object.isNumber(tab)){
			tab = this.tabs[tab];
		}	
		return tab._open;
	},
	
	// allow tab to be an element, index, or string id
	getTab: function(tab){
		if(Object.isNumber(tab)){
			tab = this.tabs[tab];
		} else if (Object.isString(tab)){
			tab = $(tab);
		}
		return tab;
	}
});

Accordion.Animation = Class.create({
	initialize: function(options){
		// option defaults
		this.options = Object.extend({
			duration: 1,
			transition: Effect.Transitions.sinoidal
		}, options || {});
		
		this.accordion = this.options.accordion;
		this.currentlyAnimating = false;
		
		// listen for custom events
		document.observe(this.accordion.accordionId+"_accordion:initialized", this.setupAnimations.bindAsEventListener(this));
		document.observe(this.accordion.accordionId+"_tab:opened", this.openAnimation.bindAsEventListener(this));
		document.observe(this.accordion.accordionId+"_tab:closed", this.closeAnimation.bindAsEventListener(this));
		document.observe(this.accordion.accordionId+"_tab:parallel", this.parallelAnimation.bindAsEventListener(this));
	},
	
	setupAnimations: function(e){
		// get active and inactive tabs from event memo
		var activeTabs = e.memo.activeTabs;
		var inactiveTabs = e.memo.inactiveTabs;
		
		// set original drawer heights to "_height" property for use in animations
		this.accordion.drawers.each(function(currentDrawer){
			currentDrawer._height = currentDrawer.getHeight();
			currentDrawer.setStyle({overflow: "hidden"});
		}.bind(this));	
		
		// set inactive tabs drawer height to 0px to appear closed and for smoothness in animation
		inactiveTabs.each(function(currentTab){
			this.accordion.drawers[this.accordion.tabs.indexOf(currentTab)].setStyle({
				height: "0px"
			});
		}.bind(this));
		
		// set active tab(s) drawer height to it's original height for smoothness in animation
		activeTabs.each(function(activeTab){
			this.accordion.drawers[this.accordion.tabs.indexOf(activeTab)].setStyle({
				height: this.accordion.drawers[this.accordion.tabs.indexOf(activeTab)]._height+"px"
			});
		}.bind(this));
	},
	
	openAnimation: function(e){
		var tabToOpen = Event.element(e);
		var drawerToOpen = this.accordion.drawers[this.accordion.tabs.indexOf(tabToOpen)];
		new Effect.Morph(
			drawerToOpen, {
				style: {height: drawerToOpen._height+"px"},
				duration: this.options.duration,
				transition: this.options.transition,
				queue: { 
					position: 'end', 
					scope: this.accordion.accordionId,
					limit: 10
				},				
				beforeStart: function(){
					//console.log(this.accordion.accordionId + ' is opening', tabToOpen);//, drawerToOpen);
					document.fire(this.accordion.accordionId + '_tab:opening', {tab: tabToOpen});
					this.currentlyAnimating = true;
				}.bind(this),
				afterFinish: function(effect){
					this.currentlyAnimating = false;
					effect.element.setStyle({'height': 'auto'});
				}.bind(this)		
			}
		);
	},
	closeAnimation: function(e){
		var tabToClose = Event.element(e);
		var drawerToClose = this.accordion.drawers[this.accordion.tabs.indexOf(tabToClose)];
		drawerToClose._height = drawerToClose.getHeight();
		
		new Effect.Morph(
			drawerToClose, {
				style: {height: "0px"},
				duration: this.options.duration,
				transition: this.options.transition,
				queue: { 
					position: 'end', 
					scope: this.accordion.accordionId,
					limit: 10
				},
				beforeStart: function(){
					document.fire(this.accordion.accordionId + '_tab:closing', {tab: tabToClose});
					this.currentlyAnimating = true;
				}.bind(this),						
				afterFinish: function(){
					this.currentlyAnimating = false;
				}.bind(this)
			}
		);
	},
	parallelAnimation: function(e){
		var tabToOpen = Event.element(e);
		var tabToClose = e.memo.tabToClose.reduce();
		var drawerToOpen = this.accordion.drawers[this.accordion.tabs.indexOf(tabToOpen)];
		var drawerToClose = this.accordion.drawers[this.accordion.tabs.indexOf(tabToClose)];
		drawerToClose._height = drawerToClose.getHeight();
		
		new Effect.Parallel([
			new Effect.Morph(
				drawerToOpen, {
					style: {height: drawerToOpen._height+"px"},
					duration: this.options.duration,
					transition: this.options.transition,
					beforeStart: function(){
						this.currentlyAnimating = true;
					}.bind(this),						
					afterFinish: function(effect){
						this.currentlyAnimating = false;
						effect.element.setStyle({'height': 'auto'});
					}.bind(this)									
				}
			),
			new Effect.Morph(
				drawerToClose, {
					style: {height: "0px"},
					duration: this.options.duration,
					transition: this.options.transition,
					beforeStart: function(){
						this.currentlyAnimating = true;
					}.bind(this),						
					afterFinish: function(){
						this.currentlyAnimating = false;
					}.bind(this)
				}
			)			
			],{
			sync: true,
			queue: { 
				position: 'end', 
				scope: this.accordion.accordionId,
				limit: 10
			}
		});
	}
});

Accordion.Controller = Class.create(Accordion, {
	initialize: function($super, options){
		// call superclass's initialize
		$super(options);

		// add event handling
		this.triggers.invoke("observe", this.options.eventType, this.getActivatedTab.bindAsEventListener(this));			
	},
	getActivatedTab: function(e){
		var activatedTrigger = Event.findElement(e, this.options.triggerSelector);
		var activatedTab = this.tabs[this.triggers.indexOf(activatedTrigger)];
		if(this.isTabDisabled(activatedTab) == true){
			return;
		}
		this.manageActivatedTab(activatedTab);
	},
	manageActivatedTab: function(activatedTab){
		// if tab is closed, close other tabs and open it, if it is open, do nothing.
		if(!activatedTab._open){ this.closeOtherTabs(activatedTab); }	
	},
	getCurrentTab: function(){
		return this.tabs.find(function(currentTab){
			return currentTab._open;
		});
	},
	getNextTab: function(){
		var currentTab = this.getCurrentTab();
		var currentIndex = this.tabs.indexOf(currentTab);
		if (currentIndex < this.tabs.length-1){
			return this.tabs[currentIndex+1];
		} else {
			return "Currently open tab is the last tab.";
		}
	},
	getPreviousTab: function(){
		var currentTab = this.getCurrentTab();
		var currentIndex = this.tabs.indexOf(currentTab);
		if (!currentIndex == 0){
			return this.tabs[currentIndex-1];;					
		} else {
			return "Currently open tab is the first tab.";
		}
	}	
});

Accordion.Controller.MultiTabAllClose = Class.create(Accordion.Controller, {
	manageActivatedTab: function(activatedTab){
		// if tab is open, close it, if tab is closed, open it.
		activatedTab._open ? this.closeTab(activatedTab) : this.openTab(activatedTab);		
	},
	getCurrentTab: function(){
		return "MultiTab Controller does not support this function.";
	},
	getNextTab: function(){
		return "MultiTab Controller does not support this function.";
	},
	getPreviousTab: function(){
		return "MultiTab Controller does not support this function.";
	}
});
// Can only open one tab at a tie, but can close all tabs
Accordion.Controller.OneTabAllClose = Class.create(Accordion.Controller, {
	manageActivatedTab: function(activatedTab){
		// if tab is open, close it
		if(activatedTab._open){
			this.closeTab(activatedTab)
			
		// if tab is closed, close all other tabs and open it
		} else if(!activatedTab._open){
			var currentlyOpenTab = this.getCurrentTab();
			if(currentlyOpenTab){
				this.closeOtherTabs(activatedTab);
			} else {
				this.openTab(activatedTab);
			}
		}		
	}
});
// Can open multiple tabs at a time and can close all tabs.
Accordion.Controller.MultiTabAllClose = Class.create(Accordion.Controller, {
	manageActivatedTab: function(activatedTab){
		// if tab is open, close it, if tab is closed, open it.
		activatedTab._open ? this.closeTab(activatedTab) : this.openTab(activatedTab);		
	},
	getCurrentTab: function(){
		return "MultiTab Controller does not support this function.";
	},
	getNextTab: function(){
		return "MultiTab Controller does not support this function.";
	},
	getPreviousTab: function(){
		return "MultiTab Controller does not support this function.";
	}
});

// This class is for filter-selection-primary on Scoreboards/Leaderboards page
var FilterSelection = Class.create({
	initialize: function(container, filterSectionId){
		this.container = $(container); // the div to show the selections (should have an ol)
		this.selectionList = this.container.down('ol');
		this.container.hide();
		
		this.filterSectionId = filterSectionId; // id of the filter section. e.g. filter-section-primary
		
		// listen to accordion events
		document.observe('accordion_tab:opening', this.__tabOpening.bindAsEventListener(this));	
		document.observe('accordion_tab:closing', this.__tabClosing.bindAsEventListener(this));	
	},
	
	// handles the custom opening event of the accordion.
	__tabOpening: function(ce){
		if(ce.memo.tab.identify() == this.filterSectionId){
			new Effect.SlideUp(this.container, {duration:0.5});
		}
	},
	
	// handles the custom closing event of the accordion
	__tabClosing: function(ce){
		/* TODO: population
			- select click handlers should call the add() method as they go to reflect the user's choices
		*/
		if(ce.memo.tab.identify() == this.filterSectionId){
			new Effect.SlideDown(this.container, {duration:0.5});
		}
	},
	
	// inserts a selection item into the ol list
	add: function(name, value){
		this.selectionList.insert(new Element('li', {id: 'selection_' + name}).update('<label>#{name}</label><p>#{value}</p>'.interpolate({'name':name, 'value': value})));
		//<li><label>Scoreboard</label><p>Circuit</p></li>
	},
	
	// removes the selection with the given name
	remove: function(name){
		var li = this.selectionList.childElements().find(function(elLi){
			return elLi.id == 'selection_' + name;
		});
		li.remove();
	},
	
	// clears all selections
	clear: function(){
		this.selectionList.update('');
	},
	
	// determines if there are any items in the list
	empty: function(){
		return this.selectionList.empty();
	}
});
