﻿/******************************
 * waTabs - Flexible and Unobtrusive Tabs with Ajax/JSON/iFrame support
 * 
 * Simple and clean tab class including support for hybrid ajax, json, and
 * iframe content. Including the ability to customize the tab transition behavior
 * or json/ajax processing functionality per tab class instance. Without
 * the need to altering any of the actual waTabs class js code.
 * 
 * @example
 * 
 * var tabs = new waTabs('tab-wrapper', {
 *     optionName: true,
 *     selectedTabClass: 'selected'
 * });
 * Object.extend(tabs, {
 *     processTab: function() {
 *         // Add code here to alter the way the tab transitions
 *     }
 * });
 * 
 * @version		v0.6
 * 
 * @author		Deavon Barlow <dbarlow [at] webassociates.com>
 * @copyright	Copyright © 2008 Web Associates. All Rights Reserved.
 ******************************/
var waTabs = new Class({

	/**
	 * Options
	 */
	options: {
		////////// SET THESE OPTIONS //////////
		// General Class Names
		tabsULClass: 'header',
		selectedTabClass: 'selected',
		tabContentsWrapperClass: 'main',
		tabContentChildInactiveClass: 'inactive',
		// Tab Navigation Class Names
		tabNavIsPresent: false,
		tabNavFirst: 't-first',
		tabNavPrev: 't-prev',
		tabNavNext: 't-next',
		tabNavLast: 't-last',
		// Anchor-Type Keys
		tabTypeKeyAttribute: 'type', // The name for the <a/> tag attribute
		jsonAnchorKey: 'json',
		ajaxAnchorKey: 'ajax',
		iframeAnchorKey: 'iframe',
		loadingIconPath: 'images/icon_loading.gif',
/*
		// Navigation Tab Options
		navigationTabs: {
			firstClass: 't-first',
			previousClass: 't-prev',
			nextClass: 't-next',
			lastClass: 't-last'
		},

		// Dynamic Tab Options
		dynamicTabs: {
			typeKeyAttribute: 'type',
			jsonKey: 'json',
			ajaxKey: 'ajax',
			iframeKey: 'iframe'
		},
*/
		processJsonResponse: Class.empty,

		// Settings
		hasSIFR: false,
		persistDynamicTabContents: false, // If false, load dynamic tab [json/ajax] contents with every click of that tab.
		useDeepLinking: true
		///////////////////////////////////////
	},

/** PRIVATE METHODS ****************************************************/

	/**
	 * Get all tab LIs within tabs UL
	 */
	_getTabs: function() {
		// gets LIs within parent UL
		this.tabs = $(this.tabsUL).getElements('li');

		// If tab navigation items are present
		if (this.options.tabNavIsPresent) {
			/**** OPTIMIZE THIS CODE ****/
			// Remove tab navigation LIs from tabs array if they exist
			try {
				this.tabs.remove(this.tabsWrapper.getElement('li.' + this.options.tabNavFirst));
			} catch(e) {}
			try {
				this.tabs.remove(this.tabsWrapper.getElement('li.' + this.options.tabNavPrev));
			} catch(e) {}
			try {
				this.tabs.remove(this.tabsWrapper.getElement('li.' + this.options.tabNavNext));
			} catch(e) {}
			try {
				this.tabs.remove(this.tabsWrapper.getElement('li.' + this.options.tabNavLast));
			} catch(e) {}
			/**** OPTIMIZE THIS CODE ****/
		}
	},

	/**
	 * Get Selected Tab
	 */
	_getSelectedTab: function() {
		// get currently selected LI based on 'selectedTabClass'
		selectedTab = this.tabsUL.getElement('li.' + this.options.selectedTabClass);

		// If it's actually an LI
		try {
			return selectedTab;
		} catch(e) {}
	},

	/**
	 * Get index of passed tab element
	 */
	_getTabIndex: function(el) {
		if (!this.tabs) { this._getTabs(); }

		// Get tab's index (i.e. 0, 2, or 5, etc)
		tabIndex = this.tabs.indexOf(el);

		if (tabIndex > -1) {
			return tabIndex;
		} else {
			return null;
		}
	},

	/**
	 * setupTabClickEvents
	 * Loop through all tab LIs and setup click events
	 */
	_setupTabClickEvents: function() {
		// Loop through each tab <li/>
		this.tabs.each(function(li, i) {
			// get first <a/> tag within <li/>
			hyperlink = li.getElement('a');

			// If hyperlink exists on current tab
			if (hyperlink) {
				// Attach onclick event listener
				hyperlink.addEvent('click', function(e) {
					// Keep the scroll position from changing on click, etc
					e = new Event(e).stop();

					// If deep-linking is enabled
					if (this.options.useDeepLinking) {
						// Add tab offset as history state
						this.historyManager.addState('tab:' + i);
					}

					// Select clicked tab
					this.selectTab(e.target.getParent(), i);
				}.bind(this));
			}
		}.bind(this));
	},

/** CONSTRUCTOR ****************************************************/

	/**
	 * Constructor
	 * 
	 * @param {String} id of the parent tab 'wrapper' which contains the entire tab block
	 * @param {Object} Options
	 */
	initialize: function(tabsWrapper, options) {
		this.setOptions(options);
		//
		if ($type(tabsWrapper) == 'element') {
			this.tabsWrapper = tabsWrapper; // DIV
		} else {
		// Set tabs wrapper DIV
			this.tabsWrapper = $(tabsWrapper);
		}

		// Set tabs UL
		this.tabsUL = this.tabsWrapper.getElement('ul.' + this.options.tabsULClass); // UL

		// If deep-linking is enabled
		if (this.options.useDeepLinking) {
			// Define the HistoryManager
			this.historyManager = new HistoryManager();
		}

		// If tabsUL exists within tabsWrapper
		if (this.tabsUL) {
			// Check if any tabs are pre-selected in static html
			// then set this.selectedTab to that element
			this.selectedTab = this._getSelectedTab();

			// gets LIs (tabs) within tabsUL
			this._getTabs();

			// Set currently selected tab's index (i.e. 0, 2, or 5, etc)
			this.selectedTabIndex = this._getTabIndex(this.selectedTab);

			// Set tabContentsWrapper
			this.tabContentsWrapper = this.tabsWrapper.getElement('div.' + this.options.tabContentsWrapperClass);

			// If tab contents wrapper div exists
			if (this.tabsWrapper.hasChild(this.tabContentsWrapper)) {
				// Get array of child DIVs within tabContentsWrapper
				this.tabContentsChildren = this.tabContentsWrapper.getChildren();

				// Setup tab click events
				this._setupTabClickEvents();

				var urlHash;
				// If deep-linking is enabled
				if (this.options.useDeepLinking) {
					// Add onHistoryChange event
					this.historyManager.addEvent('onHistoryChange', function(i) {
						// Isolate the tab index from the returned hash value
						i = i.substring(4, i.length).toInt();
						// If current hash value matches statically selected tab
						if (this.selectedTab === this.tabs[i]) {
							// Nullify currently selected tab
							this.selectedTab = null;
						}						
						// If 'i' is not set
						if (!i) i = 0;
						// Select Tab
						this.selectTab(this.tabs[i], i);
					}.bind(this));
					// Set URL hash
					urlHash = this.historyManager.getCurrentLocation();

					if (urlHash) {
						// Fire 'onHistoryChange' event
						this.historyManager.fireEvent('onHistoryChange', urlHash);
					}
				}

				// Get dynamic tab type
				dynamicTabType = this.selectedTab.getElement('a').getProperty(this.options.tabTypeKeyAttribute);

				// If pre-selected tab in static html is a dynamic tab
				if ($chk(dynamicTabType) && !urlHash) {
					// Set old tab index as current
					this.oldTabIndex = this.selectedTabIndex;
					// Get Tab Content
					this.getTabContent();
				}
			}

			// If tab navigation items are present
			if (this.options.tabNavIsPresent) {
				/**** OPTIMIZE THIS CODE ****/
				try {
					this.tabsWrapper.getElement('li.' + this.options.tabNavFirst).addEvent('click', function(e) {
						new Event(e).stop();
						this.selectFirst();
					}.bind(this));
				} catch(e) {}

				try {
					this.tabsWrapper.getElement('li.' + this.options.tabNavPrev).addEvent('click', function(e) {
						new Event(e).stop();
						this.selectPrevious();
					}.bind(this));
				} catch(e) {}

				try {
					this.tabsWrapper.getElement('li.' + this.options.tabNavNext).addEvent('click', function(e) {
						new Event(e).stop();
						this.selectNext();
					}.bind(this));
				} catch(e) {}

				try {
					this.tabsWrapper.getElement('li.' + this.options.tabNavLast).addEvent('click', function(e) {
						new Event(e).stop();
						this.selectLast();
					}.bind(this));
				} catch(e) {}
				/**** OPTIMIZE THIS CODE ****/
			}
		}
	},

/** PUBLIC METHODS ****************************************************/

	/**
	 * Select the tab via tab-index
	 * 
	 * @param {Object} Tab to select
	 * @param {Number} Tab-index of tab to select
	 */
	selectTab: function(tabLI, i) {
		// If new tab is already selected, return
		if (this.selectedTab == tabLI) { return; }

		try {
			// Unset original 'active' tab
			this.selectedTab.removeClass(this.options.selectedTabClass);
		} catch(e) {}

		// Store clicked <a/> tag as selected
		this.selectedTab = tabLI;
		// Set old tab index from original selected tab index
		this.oldTabIndex = this.selectedTabIndex;
		// Set new selected tab index to that of the clicked tab
		this.selectedTabIndex = i;

		// Set active tab
		$(this.selectedTab).addClass(this.options.selectedTabClass);

		// Get Tab Content
		this.getTabContent();
	},

	/**
	 * Get Tab Content
	 */
	getTabContent: function() {
		// Get first <a/> tag within active/selected tab LI
		tabAnchor = $(this.selectedTab).getElement('a');
		// Store 'type' attribute
		type = tabAnchor.getProperty(this.options.tabTypeKeyAttribute);
		// Store 'href' attribute
		href = tabAnchor.getProperty('href');

		// If href doesn't equal '#'
		if (href != '#') {
			// If tab is a dynamic tab
			if (type) {
				// Process dynamic tab
				this.processDynamicTab(type, href);
			}

			// If option is set to persist dynamic data (to only load once)
			if (this.options.persistDynamicTabContents) {
				// Set tab href to '#', to persist data
				tabAnchor.setProperty('href', '#');
			}
		}

		// Hide/Show tabs
		this.processTab();
	},

/** TAB TRANSITION & DYNAMIC TAB METHODS ****************************************************/

	/**
	 * Process JSON Tab
	 * 
	 * @param {String} Type of dynamic tab
	 * @param {String} URL passed from tab anchor href for JSON request
	 * @param {Object} (for future use) optional js object hash to further configure request
	 */
	processDynamicTab: function(type, url, options) {
		// Get current tab content div
		tabContentDiv = this.tabContentsChildren[this.selectedTabIndex];

		// Type of dynamic tab
		switch(type) {
			// JSON
			case this.options.jsonAnchorKey: {
				new Json.Remote(url, {
					method: 'get',
					onRequest: function() {
						// Empty tab content div
//						tabContentDiv.empty();
						// Add loading icon
						image = new Element('img', {
							'alt': 'loading icon',
							'class': 'loading_icon',
							'src': this.options.loadingIconPath
						}).inject(tabContentDiv);
					}.bind(this),
					onComplete: function(jsonObj) {
						// Remove loading icon
						$(image).remove();
						// Call custom JSON data manipulation function
						this.options.processJsonResponse(jsonObj, tabContentDiv, this.selectedTabIndex);
					}.bind(this)
				}).send();

				break;
			}

			// AJAX (XMLHttpRequest)
			case this.options.ajaxAnchorKey: {
				new Ajax(url, {
					method: 'get',
					// element to inject with response content
					update: tabContentDiv,
					onRequest: function() {
						// Empty active tab content div
						tabContentDiv.empty();
						// Add loading icon
						image = new Element('img', {
							'alt': 'loading icon',
							'class': 'loading_icon',
							'src': this.options.loadingIconPath
						}).inject(tabContentDiv);
					}.bind(this),
					onComplete: function() {
						// Remove loading icon
						$$('IMG.loading_icon').remove();
					}
				}).request();

				break;
			}

			// iFrame
			case this.options.iframeAnchorKey: {
				// If iFrame doesn't already exist within div
				if (!tabContentDiv.getElement('iframe')) {
					// Empty active tab content div
					tabContentDiv.empty();
					// Create iFrame
					tabContentDiv.iframe = new Element('iframe', {
						'src': url,
						'marginwidth':  0,
						'marginheight': 0,
						'frameBorder':  0,
						'scrolling': 'auto',
						'width': '100%'
					}).inject(tabContentDiv);
				}
/*
				// Add onload event to iframe to hide loading icon
				tabContentDiv.iframe.addEvent('load', function(e) {
					this.hideLoadingIcon.delay(150, this, this.options.loadingIconPath);
				}.bind(this));
				this.showLoadingIcon(this.options.loadingIcon);
*/
				break;
			}
		}
	},

	processTab: function(href, options) {
		hide = this.tabContentsChildren[this.oldTabIndex];
		show = this.tabContentsChildren[this.selectedTabIndex];
/*
		// Fade Out
		fadeout = new Fx.Style(hide, 'opacity', {duration: 200, fps: 10, transition: Fx.Transitions.Sine.easeIn, onComplete: function() { hide.addClass(this.options.tabContentChildInactiveClass) }});
		fadeout.start(1, 0);
		// Fade In
		fadein = new Fx.Style(show, 'opacity', {duration: 200, fps: 10, transition: Fx.Transitions.Sine.easeIn, onComplete: function() { show.removeClass(this.options.tabContentChildInactiveClass) }});
		fadein.start(0, 1);
*/
		hide.addClass(this.options.tabContentChildInactiveClass);
		show.removeClass(this.options.tabContentChildInactiveClass);
	},

/** TAB NAVIGATION METHODS ****************************************************/

	selectFirst: function() {
		try {
			first = this.tabs[0];
			this.selectTab(first, 0);
		} catch(e) {}
	},

	selectLast: function() {
		try {
			last = this.tabs[(this.tabs.length-1)];
			this.selectTab(last, this._getTabIndex(last));
		} catch(e) {}
	},

	selectPrevious: function() {
		try {
			if (!this.selectedTab || this.selectedTabIndex == 0) {
				tabIndex = 0;
			} else {
				tabIndex = this.selectedTabIndex - 1;
			}

			previous = this.tabs[tabIndex];
			this.selectTab(previous, this._getTabIndex(previous));
		} catch(e) {}
	},

	selectNext: function() {
		try {
			if (!this.selectedTab || this.selectedTabIndex == (this.tabs.length - 1)) {
				tabIndex = (this.tabs.length - 1);
			} else {
				tabIndex = this.selectedTabIndex + 1;
			}

			next = this.tabs[tabIndex];
			this.selectTab(next, this._getTabIndex(next));
		} catch(e) {}
	}
});

waTabs.implement(new Options);
