////////////////////////////////////////////////////////////////////////////////////////////////
//			PLUGIN:		ARMY KNIFE
//			VERSION:	1.5
//			AUTHOR:		ROMAN D. PACHECO
////////////////////////////////////////////////////////////////////////////////////////////////

(function($){
	var pluginName	=	"armyKnife",
		_v			=	{
			cssProps	:	{
				active	:	{
					marginLeft	:	"0%"
				},
				above	:	{
					zIndex		: 	100
				},
				below	:	{
					zIndex		: 	10
				},
				hiddenR	:	{
					marginLeft	:	"100%"
				},
				hiddenL	:	{
					marginLeft	:	"-100%"
				},
				fadeOut	:	{
					opacity	:	0
				},
				fadeIn	:	{
					opacity	:	1
				}
			},
			regex		: 	{
				transition	: 	{
					fadeIn		:	new RegExp("fade(,|$|In)","i"),
					fadeOut		:	new RegExp("fade(,|$|Out)","i"),
					slideIn		:	new RegExp("slide(,|$|In)","i"),
					slideOut	:	new RegExp("slide(,|$|Out)","i"),
					none		:	new RegExp("none(,|$)","i")
				}
			}
		},
		_e			=	{
			button	: 	$("<a />",{
				"href"	: 	"#"
			})
		},
		_s			=	{
			speed					:	300,
			sections				:	"> *",
			startingSection			:	0,
			easing					:	"swing",				//	[swing|linear]
			transition				:	"none",					//	[none|slide|slideIn|slideOut|fade|fadeIn|fadeOut]
			autoResize				: 	false,
			resizeSpeed				: 	200,
			autoRotate				:	false,
			autoRotateDelay			:	5000,
			generateNav				:	false,
			navType					:	"empty",				//	[empty|numeric|text|custom]
			navItemSource			:	function(section) {		//	REQUIRED WHEN navType = 'text' OR 'custom'; RESPECTIVE SECTION IS PASSED FOR EASY REFERENCE
				return "&nbsp;";
			},
			navItemCode				:	function(source) {		//	REQUIRED WHEN navType = 'custom'; SECTION SOURCE IS PASSED FOR EASY REFERENCE
				var btn	=	_e.button.clone();
				btn.html(source);
				return btn;
			},
			navID					:	false,
			navClass				:	pluginName + "-Nav",
			navTrigger				:	"click",
			itemsPerNav				:	0,
			activeNavItemClass		:	"active",
			showSectionButtons		: 	false,
			sectionButtonClass		: 	pluginName + "-Btn",
			sectionButtonCodeNext	: 	_e.button.clone(),
			sectionButtonCodePrev	: 	_e.button.clone(),
			sectionOnEnter			:	false,
			sectionOnExit			:	false,
			sectionBeforeEnter		:	false,
			sectionBeforeExit		:	false,
			useContinue				:	false
		},
		_m			=	{
			init	:	function(o,callback) {
				return this.each(function() {
					var $this	=	$(this),
						data	=	$this.data(pluginName);
					if(!data) {
						var s =	$.extend(true,{},_s);
						s = o ? $.extend(true,s,o) : s;
						$.extend(true,s,{
							sectionList	:	[],
							navItems	:	[],
							currentSection	:	s.startingSection,
							targetSection	:	false
						});
						$this.data(pluginName,s);
						data = $this.data(pluginName);
						var allSections	=	$this.find(data.sections);
						if(data.generateNav) {
							_m.generateNav.call($this,allSections);
						};
						$.each(allSections,function(i,e) {
							if(i == data.startingSection) {
								$(e).css(_v.cssProps.active);
							} else {
								$(e).css(_v.cssProps.hiddenR);
							};
							if(data.showSectionButtons) {
								if(i > 0) {
									var prevBtn	=	data.sectionButtonCodePrev.clone();
									prevBtn.html() == "" && prevBtn.html("Prev");
									prevBtn
										.addClass(data.sectionButtonClass)
										.bind("click",function() {
											_m.prev.call($this);
											return false;
										})
										.appendTo($(e));
								};
								if(i < allSections.length - 1) {
									var nextBtn	=	data.sectionButtonCodeNext.clone();
									nextBtn.html() == "" && nextBtn.html("Next");
									nextBtn
										.addClass(data.sectionButtonClass)
										.bind("click",function() {
											_m.next.call($this);
											return false;
										})
										.appendTo($(e));
								};
							};
							data.sectionList.push($(e));
						});
					};
					if(data.autoRotate) {
						_m.setAutoRotate.call($this);
					};
					if(data.autoResize) {
						data.targetSection = data.currentSection;
						_m.resizeView.call($this,callback);
					} else {
						if(typeof callback == "function") {
							callback();
						}
					};
								
					//	BEFORE ENTER
					data.beforeEnterComplete = new $.Deferred();
					$.when(data.beforeEnterComplete)
						.then(function() {
							
							//	ON ENTER
							if(typeof data.sectionOnEnter == "function") {
								data.sectionOnEnter.call(data.sectionList[data.currentSection],_m.getSectionCallbackObj.call($this,"onenter"));
							};
						});
					if(typeof data.sectionBeforeEnter == "function") {
						var sbeObj	=	_m.getSectionCallbackObj.call($this,"beforeenter");
						data.sectionBeforeEnter.call(data.sectionList[data.currentSection],sbeObj);
					};
					if(!data.useContinue || (typeof data.sectionBeforeEnter != "function") && data.useContinue) {
						_m.resolveContinue.call($this,"beforeenter");
					};
				});
			},
			getSectionCallbackObj	:	function(type) {
				var $this	=	this,
					data	=	$this.data(pluginName);
				if(data) {
					var obj	=	{
						"sections"	:	data.sectionList,
						"cont"	:	function() {
							_m.resolveContinue.call($this,type);
						}
					};
					switch(type) {
						case "beforeenter":
							$.extend(true,obj,{
								"current"	:	data.targetSection,
								"previous"	:	data.currentSection
							});
							break;
						case "onenter":
							$.extend(true,obj,{
								"current"	:	data.targetSection,
								"previous"	:	data.currentSection
							});
							break;
						case "beforeexit":
							$.extend(true,obj,{
								"current"	:	data.currentSection,
								"target"	:	data.targetSection
							});
							break;
						case "onexit":
							$.extend(true,obj,{
								"current"	:	data.currentSection,
								"target"	:	data.targetSection
							});
							break;
						default:
							$.extend(true,obj,{
								"current"	:	data.currentSection,
								"previous"	:	data.currentSection
							});
					}
					return obj;
				};
			},
			trigger			:	function(m) {
				var $this	=	this,
					data	=	$this.data(pluginName);
				if(data) {
					var method	=	m.toString().toLowerCase().replace(/^section/,"");
					_m.resolveContinue.call($this,method);
				};
			},
			resolveContinue	:	function(type) {
				var $this	=	this,
					data	=	$this.data(pluginName);
				if(data) {
					switch(type) {
						case "beforeenter":
							if(typeof data.beforeEnterComplete == "object") {
								data.beforeEnterComplete.resolve();
							};
							break;
						case "onenter":
							if(typeof data.onEnterComplete == "object") {
								data.onEnterComplete.resolve();
							};
							break;
						case "beforeexit":
							if(typeof data.beforeExitComplete == "object") {
								data.beforeExitComplete.resolve();
							};
							break;
						case "onexit":
							if(typeof data.onExitComplete == "object") {
								data.onExitComplete.resolve();
							};
							break;
					}
				};
			},
			generateNav	:	function(s) {
				var $this	=	this,
					data	=	$this.data(pluginName);
				if(data) {
					data.nav = $("<div />");
					var	actualItemsPerNav	=	data.itemsPerNav,
						ul 					=	$("<ul />",{
							"class"	:	data.navClass
						}),
						sections			=	s;
					if(data.itemsPerNav == 0) {
						actualItemsPerNav = s.length;
					};
					var globalI	=	0;
					while(sections.length) {
						var thisUL	=	ul.clone();
						s = sections;
						$.each(s,function(i,e) {
							if(i >= actualItemsPerNav) {
								return false;
							};
							var thisLI	=	$("<li />"),
								thisA	=	_e.button.clone(),
								e		=	s[i];
							
							switch(data.navType) {
								case "numeric":
									thisA.html(parseFloat(i + 1));
									break;
								case "text":
									var source	=	data.navItemSource($(e)) || data.navItemSource;
									thisA.html(source);
									break;
								case "custom":
									var source	=	data.navItemSource($(e)) || data.navItemSource,
										code	=	data.navItemCode(source) || data.navItemCode;
									thisA = typeof code == "object" ? code : $(code);
									break;
								default:
									thisA.html("&nbsp;");
							}
							thisUL.append(thisLI);
							thisLI.append(thisA);
							thisA.bind(data.navTrigger,{
								"i"	:	globalI
							},function(e) {
								_m["goto"].call($this,e.data.i);
								return false;
							});
							if(i == data.startingSection) {
								thisLI.addClass(data.activeNavItemClass);
							};
							data.navItems.push(thisLI);
							sections = sections.filter(function(index) {
								return sections[index] != e;
							});
							globalI++;
						});
						data.nav.append(thisUL);
					}
					if(data.nav.children().length == 1) {
						data.nav = $(data.nav.children()[0]);
					};
					if(data.navID) {
						if($("#" + data.navID).length) {
							$("#" + data.navID).replaceWith(data.nav);
						} else {
							$this.after(data.nav);
						};
						data.nav.attr("id",data.navID);
					} else {
						$this.after(data.nav);
					};
				};
			},
			setAutoRotate	:	function() {
				return this.each(function() {
					var $this	=	$(this),
						data	=	$this.data(pluginName);
					if(data) {
						clearTimeout(data.autoRotateInterval);
						data.autoRotateInterval = setTimeout(function() {
							if(typeof data.isAnimating == "undefined" || data.isAnimating.isResolved()) {
								_m.next.call($this);
							} else {
								if(!data.isAnimating.isResolved()) {
									$.when(data.isAnimating).then(function() {
										_m.next.call($this);
									});
								};
							};
						},data.autoRotateDelay);
					};
				});
			},
			resizeView	:	function(callback) {
				return this.each(function() {
					var $this	=	$(this),
						data	=	$this.data(pluginName);
					if(data) {
						var viewHeight			=	$this.height(),
							targetSectionHeight	=	data.sectionList[data.targetSection].outerHeight();
						if(viewHeight != targetSectionHeight) {
							$this.animate({
								"height"	: 	targetSectionHeight
							},data.resizeSpeed,function() {
								if(typeof callback == "function") {
									callback();
								};
							});
						} else {
							if(typeof callback == "function") {
								callback();
							};
						};
					};
				});
			},
			"goto"	:	function(target) {
				return this.each(function() {
					var $this	=	$(this),
						data	=	$this.data(pluginName);
					if(data && target != data.currentSection && (typeof data.isAnimating == "undefined" || data.isAnimating.isResolved())) {
						data.isAnimating	=	new $.Deferred();
						if(data.autoRotate) {
							_m.setAutoRotate.call($this);
						};
						data.targetSection = target;
						var direction = target < data.currentSection ? "right" : "left";
						if(data.autoResize) {
							_m.resizeView.call($this,function() {
								_m.animate.call($this,direction);
							});
						} else {
							_m.animate.call($this,direction);
						};
					};
				});
			},
			next	:	function() {
				return this.each(function() {
					var $this	=	$(this),
						data	=	$this.data(pluginName);
					if(data && data.sectionList[data.currentSection] && (typeof data.isAnimating == "undefined" || data.isAnimating.isResolved())) {
						data.isAnimating	=	new $.Deferred();
						if(data.autoRotate) {
							_m.setAutoRotate.call($this);
						};
						data.targetSection = data.sectionList[data.currentSection + 1] ? data.currentSection + 1 : 0;
						if(data.autoResize) {
							_m.resizeView.call($this,function() {
								_m.animate.call($this);
							});
						} else {
							_m.animate.call($this);
						};
					};
				});
			},
			prev	:	function() {
				return this.each(function() {
					var $this	=	$(this),
						data	=	$this.data(pluginName);
					if(data && data.sectionList[data.currentSection] && (typeof data.isAnimating == "undefined" || data.isAnimating.isResolved())) {
						data.isAnimating	=	new $.Deferred();
						if(data.autoRotate) {
							_m.setAutoRotate.call($this);
						};
						data.targetSection = data.sectionList[data.currentSection - 1] ? data.currentSection - 1 : data.sectionList.length - 1;
						if(data.autoResize) {
							_m.resizeView.call($this,function() {
								_m.animate.call($this,"right");
							});
						} else {
							_m.animate.call($this,"right");
						};
					};
				});
			},
			animate	:	function(dir,callback) {
				callback = !!callback ? callback : function() {};
				return this.each(function() {
					var $this	=	$(this),
						data	=	$this.data(pluginName);
					dir = !!dir ? dir : "left";
					if(data && data.sectionList[data.currentSection] && data.sectionList[data.targetSection]) {
							
						//	BEFORE EXIT
						data.beforeExitComplete = new $.Deferred();
						$.when(data.beforeExitComplete)
							.then(function() {
								
								//	BEFORE ENTER
								data.beforeEnterComplete = new $.Deferred();
								$.when(data.beforeEnterComplete)
									.then(function() {
								
										data.sectionList[data.currentSection].css(_v.cssProps.below);
										var currentCSSProps	=	_v.cssProps.active;
										if(data.transition.match(_v.regex.transition.slideOut) || data.transition.match(_v.regex.transition.none)) {
											currentCSSProps	=	dir == "left" ? _v.cssProps.hiddenL : _v.cssProps.hiddenR;
										};
										var animObjCurrent	=	$.extend(true,{},currentCSSProps);
										!!data.transition.match(_v.regex.transition.fadeOut) && $.extend(true,animObjCurrent,_v.cssProps.fadeOut);
										
										//	ANIMATE CURRENT SECTION OUT
										if(data.transition.match(_v.regex.transition.none)) {
											data.sectionList[data.currentSection].css(animObjCurrent);
											callback();
										} else {
											data.sectionList[data.currentSection].animate(animObjCurrent,data.speed,data.easing,callback);
										};
										
										if(data.transition.match(_v.regex.transition.fadeIn)) {
											data.sectionList[data.targetSection].css(_v.cssProps.fadeOut);
										} else {
											data.sectionList[data.targetSection].css(_v.cssProps.fadeIn);
										};
										
										data.sectionList[data.targetSection].css(_v.cssProps.above);
										var resetCSSProps	=	_v.cssProps.active;
										if(data.transition.match(_v.regex.transition.slideIn) || data.transition.match(_v.regex.transition.none)) {
											resetCSSProps = dir == "left" ? _v.cssProps.hiddenR : _v.cssProps.hiddenL;
										};
										data.sectionList[data.targetSection].css(resetCSSProps);
										var animObjTarget	=	$.extend(true,{},_v.cssProps.active);
										!!data.transition.match(_v.regex.transition.fadeIn) && $.extend(true,animObjTarget,_v.cssProps.fadeIn);
										
										//	ANIMATE TARGET SECTION IN
										if(data.transition.match(_v.regex.transition.none)) {
											data.sectionList[data.targetSection].css(animObjTarget);
											data.isAnimating.resolve();
											callback();
											
											//	ON EXIT
											data.onExitComplete = new $.Deferred();
											$.when(data.onExitComplete)
												.then(function() {
											
													//	ON ENTER
													if(typeof data.sectionOnEnter == "function") {
														data.sectionOnEnter.call(data.sectionList[data.targetSection],_m.getSectionCallbackObj.call($this,"onenter"));
													};
												});
											if(typeof data.sectionOnExit == "function") {
												data.sectionOnExit.call(data.sectionList[data.currentSection],_m.getSectionCallbackObj.call($this,"onexit"));
											};
											if(!data.useContinue || (typeof data.sectionOnExit != "function" && data.useContinue)) {
												_m.resolveContinue.call($this,"onexit");
											};
										} else {
											data.sectionList[data.targetSection].animate(animObjTarget,data.speed,data.easing,function() {
												data.isAnimating.resolve();
												callback();
											
												//	ON EXIT
												data.onExitComplete = new $.Deferred();
												$.when(data.onExitComplete)
													.then(function() {
												
														//	ON ENTER
														if(typeof data.sectionOnEnter == "function") {
															data.sectionOnEnter.call(data.sectionList[data.targetSection],_m.getSectionCallbackObj.call($this,"onenter"));
														};
													});
												if(typeof data.sectionOnExit == "function") {
													data.sectionOnExit.call(data.sectionList[data.currentSection],_m.getSectionCallbackObj.call($this,"onexit"));
												};
												if(!data.useContinue || (typeof data.sectionOnExit != "function" && data.useContinue)) {
													_m.resolveContinue.call($this,"onexit");
												};
											});
										};
										if(data.nav) {
											data.nav
												.find("." + data.activeNavItemClass)
												.removeClass(data.activeClass);
											data.navItems[data.targetSection].addClass(data.activeNavItemClass);
										};
										data.currentSection = data.targetSection;
									});
								if(typeof data.sectionBeforeEnter == "function") {
									data.sectionBeforeEnter.call(data.sectionList[data.targetSection],_m.getSectionCallbackObj.call($this,"beforeenter"));
								};
								if(!data.useContinue || (typeof data.sectionBeforeEnter != "function" && data.useContinue)) {
									_m.resolveContinue.call($this,"beforeenter");
								};
							});
						if(typeof data.sectionBeforeExit == "function") {
							data.sectionBeforeExit.call(data.sectionList[data.currentSection],_m.getSectionCallbackObj.call($this,"beforeexit"));
						};
						if(!data.useContinue || (typeof data.sectionBeforeExit != "function" && data.useContinue)) {
							_m.resolveContinue.call($this,"beforeexit");
						};
					};
				});
			}
		};
	$.fn[pluginName] = function(m) {
		
		// IF THE METHOD PASSED EXISTS...
		if(_m[m]) {
			
			// RETURN THE METHOD AND ANY ATTACHED ARGUMENTS
			return _m[m].apply(this,Array.prototype.slice.call(arguments,1));
		
		// IF NOTHING IS PASSED OR NO METHOD IS PASSED BUT AN OBJECT IS PASSED...
		} else if (!m || typeof m == "object") {
			
			// RUN THE init METHOD BY DEFAULT AND PASS ANY ATTACHED ARGUMENTS
			return _m.init.apply(this,arguments);
			
		// IF WHAT IS PASSED DOESNT APPLY...
		} else {
			
			// SHOW AN ERROR
			$.error(_v.pluginName + ": Invalid method passed");			
		};
	};
})(jQuery);

