/*
 * Author:	JVK
 * Info:	Add dynamic interactivity to the side menu.
 */
(function () {
	var
		/* Cache for DOM elements. */
		oElemCache = {},
		
		/* Cache for timers */
		oTimers = {},
		
		/* Configurable settings. */
		Config = {
			sideMenuId:				"sideMenu",
			linkToggleId:			"toggleSideMenuLink",
			linkToggleImageId:		"toggleSideMenuImage",
			sideMenuExpandedClass:	"sideMenuExpanded",
			sideMenuCollapsedClass:	"sideMenuCollapsed",
			linkExpandedClass:		"toggleSideMenuLinkExpanded",
			linkExpandSource:		"/media/img/side_link_expand.gif",
			linkExpandInfo:			"Uitklappen",
			linkCollapseId:			"collapseSideMenu",
			linkCollapseSource:		"/media/img/side_link_collapse.gif",
			linkCollapseInfo:		"Inklappen"
		},
			
		/* Common (cross browser) functions. */
		Fn = {
			/* DOM functions to get elements and manage events and classes etc. */
			Dom: {
				/* getById */
				$: function (sId, bRefresh) {
					if (bRefresh || !oElemCache[sId]) {
						oElemCache[sId] = document.getElementById(sId);
					}
	
					return oElemCache[sId];
				},
				
				/*
				 * CSS class functions.
				 * Description: Manage elements' CSS classNames.
				 */
				getClassList: function (el) {
					return (el.className && el.className.length) ? el.className.split(/\s+/) : [];
				},
				
				setClassList: function (el, aClassList) {
					el.className = aClassList.join(" ");
				},

				removeClass: function (el, sClass) {
					var
						aClassList = Fn.Dom.getClassList(el),
						index;
					
					if ((index = aClassList.indexOf(sClass)) !== -1) {
						delete aClassList[index];
						Fn.Dom.setClassList(el, aClassList);
					}
				},
				
				addClass: function (el, sClass) {
					var
						aClassList = Fn.Dom.getClassList(el),
						index;
						
					if ((index = aClassList.indexOf(sClass)) === -1) {
						aClassList.push(sClass);
		

						Fn.Dom.setClassList(el, aClassList);
					}
				},
				
				replaceClass: function (el, sClass, sClassAlt) {
					Fn.Dom.removeClass(el, sClass);
					Fn.Dom.addClass(el, sClassAlt);
				},
	
				toggleClass: function (el, sClass, sClassAlt) {
					var
						aClassList = Fn.Dom.getClassList(el),
						index;
						
					if ((index = aClassList.indexOf(sClass)) !== -1) {
						Fn.Dom.replaceClass(el, sClass, sClassAlt);
					} else {
						Fn.Dom.replaceClass(el, sClassAlt, sClass);
					}
				},
				
				/*
				 * Event functions.
				 * Description: Add eventlisteners to DOM elements to enforce unobtrusive js.
				 */
				addEvent: function (obj, evType, fn, useCapture) {
					useCapture = useCapture || false;
					
					if (obj.addEventListener) {
						obj.addEventListener(evType, fn, useCapture);
						return true;
					} else if (obj.attachEvent) {
						obj["e" + evType + fn] = fn;
						obj[evType + fn] = function () {
							obj["e" + evType + fn](window.event);
						};
						var r = obj.attachEvent("on" + evType, obj[evType + fn]);
						return r;
					} else {
						obj["on" + evType] = fn;
						return true;
					}
				},
				
				removeEvent: function (obj, evType, fn) {
					if (obj.detachEvent) {
						obj.detachEvent("on" + evType, obj[evType + fn]);
						obj[evType + fn] = null;
					} else {
						obj.removeEventListener(evType, fn, false);
					}
				},
				
				cancelEvent: function (e) {
					var evt = e || window.event;
					
					evt.cancelBubble = true;
					evt.returnValue = false;
					
					if (evt.stopPropagation) {
						evt.stopPropagation();
						evt.preventDefault();
					}
					
					return false;
				},
				
				/* Deprecated, use jQuery or domReady method in stead. */
				addLoadEvent: function (f) {
					var oldOnload = window.onload;
					
					if (typeof window.onload !== "function") {
						window.onload = f;
					}
					else {
						window.onload = function () {
							oldOnload();
							f();
						};
					}
				}
			}
		},
			
		Fx = {
			/* Easing functions.
			 * t: Current time (ms)
			 * b: Beginning value
			 * c: Change in value
			 * d: Duration (ms)
			 */
			Easing: {
				easeInOutSine: function (t, b, c, d) {
					return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
				}
			},
			
			/* Animate menu slider. */
			animate: function (iTime, bShow) {
				var elSide			= Fn.Dom.$(Config.sideMenuId),			// The menu div.
					elLink			= Fn.Dom.$(Config.linkToggleId),		// The link that toggles the animation.
					iFps			= 30,									// Frames per second for the animation.
					iFrameInterval	= 1000 / iFps,							// The time per frame in ms.
					iDx				= -(elSide.offsetWidth),				// The distance to move in pixels.
					iFrame			= 0,									// Reset current frame counter.
					iFrames			= Math.ceil(iTime / 1000 * iFps);		// Number of frames the animation will run.

				// Clear running animation.
				if (oTimers.slideMenu) {
					clearInterval(oTimers.slideMenu);
				}
				
				
				if (bShow) {
					/* Animtion to show the menu (slide in to left). */
					elSide.style.marginLeft	= 0;
					elSide.style.clip		= "rect(0 0 auto auto)";
					elLink.style.right		= 0;
					
					oTimers.slideMenu = setInterval (function () {
						// Calculate the distance to move in this frame interval.
						var iDelta				= Fx.Easing.easeInOutSine(iFrameInterval * ++iFrame, 0, iDx, iTime);
						
						// Apply changes to the elements' styling.
						elSide.style.marginLeft = iDelta + "px";
						elSide.style.clip		= "rect(0 " + Math.abs(iDelta) + "px auto auto)";
						elLink.style.right		= (Math.abs(iDelta)) + "px";
						
						// Check if the animation is completed.
						if (iFrame === iFrames) {
							clearInterval(oTimers.slideMenu);

							elLink.style.right = (parseInt(elLink.style.right, 10) - parseInt(document.defaultView ? 
								getComputedStyle(Fn.Dom.$(Config.sideMenuId), null).borderLeftWidth :
								Fn.Dom.$(Config.sideMenuId).currentStyle ?
									Fn.Dom.$(Config.sideMenuId).currentStyle.borderLeftWidth :
									Fn.Dom.$(Config.sideMenuId).style.borderLeftWidth, 10)) + "px";
						}
					}, iFrameInterval);
				} else {
					/* Animation to hide the menu (slide out to the right). */
					oTimers.slideMenu = setInterval (function () {
						// Calculate the distance to move in this frame interval.
						var iDelta				= Fx.Easing.easeInOutSine(iFrameInterval * ++iFrame, iDx, -iDx, iTime);

						// Apply changes to the elements' styling.
						elSide.style.marginLeft = iDelta + "px";
						elSide.style.clip		= "rect(0 " + Math.abs(iDelta) + "px auto auto)";
						elLink.style.right		= (Math.abs(iDelta)) + "px";
						
						// Check if the animation is completed.
						if (iFrame === iFrames) {
							clearInterval(oTimers.slideMenu);
						}
					}, iFrameInterval);
				}
			}
		};
	
	/*
	 * IE Compatiblity.
	 */
	if (!Array.prototype.indexOf) {
		Array.prototype.indexOf = function (elt /*, from*/) {
			var
				len = this.length,
				from = Number(arguments[1]) || 0;

			from = (from < 0) ? Math.ceil(from) : Math.floor(from);

			if (from < 0) {
				from += len;
			}

			for (; from < len; from++) {
				if (from in this && this[from] === elt) {
					return from;
				}
			}

			return -1;
		};
	}

	/*
	 * Set page load functions.
	 */
	var slideMenuLoader = function () {
		if (Fn.Dom.$(Config.sideMenuId)) {
			Fn.Dom.replaceClass(Fn.Dom.$(Config.sideMenuId), Config.sideMenuExpandedClass, Config.sideMenuCollapsedClass);
			
			var
				elToggleMenuLink	= document.createElement("div"),
				elToggleMenuImage	= document.createElement("img");
			
			// Create images for the link that toggles the menu.
			elToggleMenuImage.id	= Config.linkToggleImageId;
			elToggleMenuImage.src	= Config.linkExpandSource;
			elToggleMenuImage.alt	= Config.linkExpandInfo;

			// Create links that toggles the menu.
			elToggleMenuLink.id		= Config.linkToggleId;
	
			// Add images to the links.
			elToggleMenuLink.appendChild(elToggleMenuImage);
			
			// Add click event to toggle menu.
			Fn.Dom.addEvent(elToggleMenuLink, "click", function (e) {
				if (oTimers.toggleLink) {
					clearTimeout(oTimers.toggleLink);
				}
			
				if (Fn.Dom.getClassList(Fn.Dom.$(Config.sideMenuId)).indexOf(Config.sideMenuCollapsedClass) > -1) {
					// Start showing animation.
					Fx.animate(300, true);

					elToggleMenuImage.src			= Config.linkCollapseSource;
					elToggleMenuImage.alt			= Config.linkCollapseInfo;
				
					/*
					oTimers.toggleLink = setTimeout(function () {
						Fn.Dom.addClass(Fn.Dom.$(Config.linkToggleId), Config.linkExpandedClass)
					}, 500);
					*/
				} else {
					// Start hiding animation.
					Fx.animate(300, false);
					
					elToggleMenuImage.src			= Config.linkExpandSource;
					elToggleMenuImage.alt			= Config.linkExpandInfo;
					
					/*
					Fn.Dom.removeClass(Fn.Dom.$(Config.linkToggleId), Config.linkExpandedClass)
					*/
				}
				
				Fn.Dom.toggleClass(Fn.Dom.$(Config.sideMenuId), Config.sideMenuCollapsedClass, Config.sideMenuExpandedClass);
			});
			
			// Add links to the DOM.
			Fn.Dom.$(Config.sideMenuId).parentNode.insertBefore(elToggleMenuLink, Fn.Dom.$(Config.sideMenuId));
		}
	};
	
	// Check what method to use to initiate our function on page load.
	if (typeof(jQuery) !== "undefined" && !/load=domready/gi.test(location.search) && !/load=addload/gi.test(location.search)) {
		// Use jQuery to check for DOMContentLoaded event.
		jQuery(document).ready(slideMenuLoader);
	} else if (typeof(domReady) !== "undefined" && !/load=addload/gi.test(location.search)) {
		// Use new domReady function to check for DOMContentLoaded event.
		domReady(slideMenuLoader);
	} else {
		// Use old addLoadEvent function to check for DOMContentLoaded event (slower).
		Fn.Dom.addLoadEvent(slideMenuLoader);
	}
}());