/**
 * LBi Tabs module
 *
 * @module    tabs
 * @version   1.10.100216
 * @requires  jQuery, LBi
 * @author    LBi Lost Boys
 */
LBi.Tabs = (function($){

	/**
	 * The Tabs class manages one or more tab menus on a given page. A tab menu typically consists
	 * of a unordered list of links, which can be linked to individual tabs using an anchor; the 
	 * #hash value must correspond to the ID of the related tab. A rel attribute with (default) 
	 * value "tab" is required on links for the Tabs class to identify them. 
	 *
	 * @class LBi.Tabs
	 * @constructor
	 * @extends LBi.DOMListener
	 * @param {Object} settings Settings, see LBi.Tabs.Defaults
	 * @return {Tabs} Tabs instance
	 */
	var Tabs = LBi.Class.extend(
		LBi.DOMListener,
		
		function(settings) {
			this.settings = $.extend({}, Tabs.Defaults, settings);

			var options = this.settings;
			var relations = options.relations || new LBi.LinkRelations();
			var regex = new RegExp('(^|\\s)' + options.tabRelation + '(\\s|$)');
			
			relations.subscribe(regex, this.handleClick.bind(this));

			this.setup(document);
			if(options.hashEnabled) {
				this.pollHash();
			}
		},{

		/**
		 * Applies the active state of tabs within a given node.
		 * @private
		 */
		setup:function(root) {
			var settings = this.settings;
			var activated = $(
				settings.tabSelector + '.' + 
				settings.activeClass + ' a[rel=' + 
				settings.tabRelation + ']', root
			);

			for(var i=0; i<activated.length; i++) {
				this.activate(activated[i], true);
			}
		},

		/**
		 * Click handler, called via the relation attribute
		 * @private
		 */
		handleClick:function(e) {
			this.activate(e.target);
			e.preventDefault();
		},

		/**
		 * This method is called after a DOMNodeInserted event was fired, and searches the 
		 * inserted html for tabs in order to apply any encountered "active" states.
		 * 
		 * @param {Event} e Event object
		 */
		nodeInserted:function(e) {
			if(this.settings.ajaxEnabled) {
				this.setup(e.target);
			}
		},

		/**
		 * NodeRemoved is not used, because nothing has to be cleaned up.
		 * @param {Event} e Event object
		 */
		nodeRemoved:function(e) {
		},

		/**
		 * Activates or toggles a tab based on the given link. The related tab (ID referenced via its hash) 
		 * is made visible. All other tabs of links in the same tabmenu are hidden. This method
		 * is called automatically, but may also be called manually when needed. A "layoutchanged"
		 * event is fired for each tab.
		 * 
		 * @param {Node} link The link to activate. MUST be contained in a tab menu.
		 */
		activate:function(link, overruled) {
			var settings = this.settings;
			var tabs = $(link).closest(settings.menuSelector);
			var links = tabs.find('a[rel=' + settings.tabRelation + ']');
			var active = this.settings.activeClass;
			var toggles = !overruled && this.settings.toggleEnabled;

			for(var i=0; i<links.length; i++) {
				var tab = links[i];
				var $item = $(tab).closest(settings.tabSelector);
				var isActive = toggles && $item.hasClass(active);

				if(tab === link && !isActive) {
					$item.addClass(active);
				} else {
					$item.removeClass(active);
				}

				if(settings.hashEnabled) {
					var hash = tab.hash;
					var target = $(hash)[0];
					if(!target) {
						LBi.log('tab "'+ hash +'" was not found!');
						return;
					}

					var toggle = $item.hasClass(active);
					settings.animation.run(target, toggle, {
						duration: settings.animationTime,
						complete: function() {
							LBi.Dispatcher.fire(LBi.Event.LAYOUT_CHANGED, target);
						}
					});
				}
			}
		},

		/**
		 * Polls the window location hash for a reference to a tab, and activates it when encountered.
		 * This method is called once automatically for every instance of Tabs.
		 * @method pollHash
		 */
		pollHash:function() {
			var hash = window.location.hash;
			if(hash) {
				this.activateHash(hash);
			}
		},

		/**
		 * Polls the document for a tab link with the given hash, and activates it when encountered.
		 * 
		 * @param {String} hash
		 */
		activateHash: function(hash) {
			var link = $('a[rel=' + this.settings.tabRelation + ']').filter('[href$='+hash+']');
			if(link.length) {
				this.activate(link[0]);
			}
		}
	});

	/**
	 * Default settings for tabs instances. 
	 * 
	 * @static
	 * @class LBi.Tabs.Defaults
	 */
	Tabs.Defaults = {
		/**
		 * Tab menu selector, used while traversing upward from a clicked tab link. 
		 * @property menuSelector
		 * @type String
		 * @default "ul"
		 */
		menuSelector: 'ul',
		
		/**
		 * Tab selector, used while traversing upward from a clicked tab link. 
		 * @property tabSelector
		 * @type String
		 * @default "li"
		 */
		tabSelector: 'li',
		
		/**
		 * Class for an active tab, for styling purposes. Applied on the node selected by the tabSelector property.
		 * @property activeClass
		 * @type String
		 * @default "active"
		 */
		activeClass: 'active',
		
		/**
		 * Rel value for tab links.
		 * @property tabRelation
		 * @type String
		 * @default "tab"
		 */
		tabRelation: 'tab',
		
		/**
		 * Defines whether hash values are used at all. If not, clicking a tab only toggles the active class, and visibility must be toggled using css.
		 * @property hashEnabled
		 * @type boolean
		 * @default true
		 */
		hashEnabled: true,
		
		/**
		 * Defines whether Tabs instances respond to DOMNodeInserted events.
		 * @property ajaxEnabled
		 * @type boolean
		 * @default true
		 */
		ajaxEnabled: true,


		/**
		 * Defines whether a second click closes an active item.
		 * @property toggleEnabled
		 * @type boolean
		 * @default false
		 * 
		 */
		toggleEnabled: false,

		/**
		 * Animation for toggling tabs. See the the LBi.Animation class.
		 * @property animation
		 * @type LBi.Animation
		 * @default LBi.Animation.TOGGLE
		 */
		animation: LBi.Animation.TOGGLE,

		/**
		 * The duration for the animation (if any)
		 * @property animationTime
		 * @type number
		 * @default 0
		 */
		animationTime: 0,
		
		/**
		 * Reference to a shared LBi.LinkRelations instance. An instance is automatically
		 * created if this property is left to null.
		 * @property relations
		 * @type Object
		 * @default null
		 */
		relations: null
	};

	return Tabs;

})(jQuery);
