/*
name			: Class Behaviour
update			: 20070705
author			: Maurice van Creij
dependencies		: lib_classbehaviour.js
info				: http://www.woollymittens.nl/content/details.asp?id=20040805133501
*/
	// MAIN
	// main class-behaviour object
	function ClassBehaviour(){
		/* properties */
			this.handlers			=	new Array();
		/* methods */
			// parse the document for classnames
			this.parseDocument		=	function(node){
											target = (node) ? node : document;
											// get all document nodes
											var allNodes = (target.all) ? target.all : target.getElementsByTagName("*");
											// for all tags
											for(var a=0; a<allNodes.length; a++){
												// if the item has a className
												if(allNodes[a].className){
													// get the classname
													nodeClass = allNodes[a].className;
													// for all behaviours
													for(var b=0; b<this.handlers.length; b++){
														// if the behaviour's name exists in the class name, apply it's events
														if(nodeClass.indexOf(this.handlers[b].name)>-1) this.handlers[b].start(allNodes[a]);
													}
												}
											}
										}
			// (cross)fader and pseudo event handler
			//this.fader				=	new Fader;
			// helper functions
			this.utilities			=	new Utilities;
	}
	// create the main class-behaviour object
	var classBehaviour = new ClassBehaviour;
	
	// UTILITIES
		function Utilities(){
			this.screenHeight 		= 	function(){
											return (window.innerHeight) ? window.innerHeight : (document.documentElement.clientHeight) ? document.documentElement.clientHeight : document.body.clientHeight ;
										}
			// return a parameter from the url's query strings
			this.getQueryParameter 	= 	function(paramName, defaultValue){
											// split the query string at the parameter name
											var queryParameters = document.location.search.split(paramName+"=");
											// split the parameter value from the rest of the string
											var queryParameter = (queryParameters.length>1) ? queryParameters[1].split("&")[0] : null ;
											// return the value
											return (queryParameter!=null) ? queryParameter : defaultValue ;
										}
			// returns a string of parameters found in the classname which can be [eval]uated
			this.getClassParameter	=	function(targetNode, paramName, defaultValue){
											// get the class parameter from the classname
											var classParameter = targetNode.className;
											// split the classname between the parameter name
											classParameter = classParameter.split(paramName + '_');
											// split the second piece between spaces and take the first part,  if there are two pieces
											classParameter = (classParameter.length>1) ? classParameter[1].split(' ')[0] : null ;
											// return the value
											return (classParameter!=null) ? classParameter : defaultValue ;
										}
			// get the previous node without worrying about text nodes
			this.nextNode			=	function(node){
											// look for the next html node
											do {
												node = node.nextSibling;
											} while(node.nodeName.indexOf('#text')>-1);
											// return it
											return node;
										}
			// get the next node without worrying about text nodes
			this.previousNode		=	function(node){
											// look for the previous html node
											do {
												node = node.previousSibling;
											} while(node.nodeName.indexOf('#text')>-1);
											// return it
											return node;
										}
			// returns the visible display state needed for this element
			this.getVisibleState	=	function(node){
											// what kind of node is this
											switch(node.nodeName.toLowerCase()){
												case 'table' : visibleState='table' ; break;
												case 'thead' : visibleState='table-header-group' ; break;
												case 'tfoot' : visibleState='table-footer-group' ; break;
												case 'tbody' : visibleState='table-row-group' ; break;
												case 'tr' : visibleState='table-row' ; break;
												case 'td' : visibleState='table-cell' ; break;
												case 'th' : visibleState='table-cell' ; break;
												default : visibleState='block';
											}
											// apply the state
											return (document.all && navigator.userAgent.indexOf('Opera')<0) ? 'block' : visibleState;
										}
		}
		
	// BEHAVIOURS
	
	// replace in class
		// define this class behaviour
		function ClassMouseHover(){
			/* properties */
			this.name 		= 	'classMouseHover';
			/* methods */
			this.start		=	function(node){
									node.onmouseover = this.addHover;
									node.onmouseout = this.remHover;
								}
			this.hasNoStateClass 	= 	function(objNode){
											return (objNode.className.indexOf('link')<0 && objNode.className.indexOf('hover')<0 && objNode.className.indexOf('active')<0);
										}
			/* events */
			this.addHover 	= 	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									var cmh = classBehaviour.classMouseHover;
									// replace link by hover
									objNode.className = (cmh.hasNoStateClass(objNode)) ? 'hover ' + objNode.className : objNode.className.replace('link','hover') ;
									// if there is an image within, replace it with the hover state too
									allImages = objNode.getElementsByTagName('IMG');
									if(allImages.length>0) allImages[0].src = allImages[0].src.replace('_link','_hover');
								}
			this.remHover 	= 	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									var cmh = classBehaviour.classMouseHover;
									// replace hover by link
									objNode.className = (cmh.hasNoStateClass(objNode)) ? 'link ' + objNode.className : objNode.className.replace('hover','link') ;
									// if there is an image within, replace it with the hover state too
									allImages = objNode.getElementsByTagName('IMG');
									if(allImages.length>0) allImages[0].src = allImages[0].src.replace('_hover','_link');
								}
			this.addActive 	= 	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									var cmh = classBehaviour.classMouseHover;
									// replace link by active
									objNode.className = objNode.className.replace('link','active') ;
									// replace hover by active
									objNode.className = objNode.className.replace('hover','active') ;
									// if there's still no active class
									if(cmh.hasNoStateClass(objNode)) objNode.className = 'active ' + objNode.className;
								}
		}
		// add this function to the classbehaviour object
		classBehaviour.classMouseHover = new ClassMouseHover;
		classBehaviour.handlers[classBehaviour.handlers.length] = classBehaviour.classMouseHover;
		
	// replace in src sub-string
		// define this class behaviour
		function SrcMouseHover(){
			/* properties */
			this.name 			= 	'srcMouseHover';
			this.cache 			= new Array();
			/* methods */
			this.start			=	function(node){
										this.cacheImages(node);
										node.onmouseover = this.addHover;
										node.onmouseout = this.remHover;
									}
			this.cacheImages	 = 	function(that) {
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										// if this is not the image, it must be the parent
										if(!objNode.src) objNode = objNode.getElementsByTagName('IMG')[0];
										// replace link by hover
										var cacheIdx = this.cache.length;
										// hover version
										this.cache[cacheIdx] = new Image();
										this.cache[cacheIdx].src = objNode.src.replace('_link','_hover');
										// active version
										this.cache[cacheIdx+1] = new Image();
										this.cache[cacheIdx+1].src = objNode.src.replace('_link','_active');
									}
			/* events */
			this.addActive 	= 	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									// if this is not the image, it must be the parent
									if(objNode.nodeName!='IMG') objNode = objNode.getElementsByTagName('IMG')[0];
									// replace link by active
									objNode.src = objNode.src.replace('_link','_active');
									// replace hover by active
									objNode.src = objNode.src.replace('_hover','_active');
								}
			this.addHover 	= 	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									// if this is not the image, it must be the parent
									if(!objNode.src) objNode = objNode.getElementsByTagName('IMG')[0];
									// replace link by hover
									objNode.src = objNode.src.replace('_link','_hover');
								}
			this.remHover 	= 	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									// if this is not the image, it must be the parent
									if(!objNode.src) objNode = objNode.getElementsByTagName('IMG')[0];
									// replace link by hover
									objNode.src = objNode.src.replace('_hover','_link');
								}
		}
		// add this function to the classbehaviour object
		classBehaviour.srcMouseHover = new SrcMouseHover;
		classBehaviour.handlers[classBehaviour.handlers.length] = classBehaviour.srcMouseHover;
		
	// Make foldout menu
		// define this class behaviour
		function FoldOutMenu(){
			/* properties */
			this.name 		= 	'foldOutMenu';
			this.timeout	=	null;
			this.delay		=	100;
			this.foldIns 	= 	new Array();
			this.activeNode =	null;
			/* methods */
			this.start		=	function(node){
									this.process(node);
								}
			this.process 	= 	function(objNode){
									var objNodes, objMatch;
									var mau = classBehaviour.matchActiveUrl;
									// apply events to all LIs
									objNodes = objNode.getElementsByTagName('LI');
									for(var intNode=0; intNode<objNodes.length; intNode++){
										// mouseover events
										objNodes[intNode].onmouseover	= this.addHover;
										objNodes[intNode].onmouseout	= this.remHover;
									}
									// apply default settings to all As
									objNodes = objNode.getElementsByTagName('A');
									for(var intNode=0; intNode<objNodes.length; intNode++){
										// if the item has no valid link
										if(objNodes[intNode].href.indexOf("#")==objNodes[intNode].href.length-1){
											// make it the link of it's first child
											objNodes[intNode].href = objNodes[intNode+1].href
										}
										// use matchActiveUrl to find active items OR use the ones marked manualy
										objMatch = (mau.process(objNodes[intNode]) || objNodes[intNode].className.indexOf('active')>-1) ? objNodes[intNode] : objMatch ;
									}
									// recurse back the last matching node
									if(objMatch!=null){
										this.recurse(objMatch.parentNode);
									}
								}
			this.recurse 	= 	function(objParentNode){
									var cmh = classBehaviour.classMouseHover;
									// add active class to the anchor
									if(objParentNode.getElementsByTagName('A').length>0) cmh.addActive(objParentNode.getElementsByTagName('A')[0]);
									// store the active node
									this.activeNode = objParentNode;
									// add the active src to any image in the node
									imgNodes = objParentNode.getElementsByTagName('img');
									if(imgNodes.length>0) imgNodes[0].src = imgNodes[0].src.replace('_link','_active');
									// next recursion to same node type
									if(objParentNode.nodeName == objParentNode.parentNode.parentNode.nodeName) this.recurse(objParentNode.parentNode.parentNode);
								}
			/* events */
			this.addHover 	= 	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									var cmh = classBehaviour.classMouseHover;
									var fom = classBehaviour.foldOutMenu;
									// if a delay is required
									if(fom.delay>0){
										// cancel the timeout on the delayed mouseevents
										clearTimeout(fom.timeout);
										// handle the delayed mouseouts
										while(fom.foldIns.length>0){
											// change the stored active node to "active"
											if(fom.activeNode!=null) if(fom.activeNode.getElementsByTagName('IMG').length>0) fom.activeNode.getElementsByTagName('IMG')[0].src = fom.activeNode.getElementsByTagName('IMG')[0].src.replace('_link','_active');
											// change the src of a child image
											imgNodes = fom.foldIns[fom.foldIns.length-1].getElementsByTagName('img');
											if(imgNodes.length>0) imgNodes[0].src = imgNodes[0].src.replace('_hover','_link');
											// remove the active classes from every item to be folded in
											cmh.remHover(fom.foldIns[fom.foldIns.length-1]);
											fom.foldIns.length = fom.foldIns.length - 1;
										}
										// restore all select form elements
										allSelects = document.getElementsByTagName('SELECT');
										if(document.all){
											for(var a=0; a<allSelects.length; a++){
												allSelects[a].style.visibility = 'visible';
											}
										}
									}
									// is the node exists
									if(objNode!=null){
										// emulate the parent node's mouseout event
										cmh.addHover(objNode);
										/*allULs = objNode.getElementsByTagName('UL');
										if(allULs.length>0){
										   alert(allULs[0].outerHTML);
										}*/
										now =0;
										ow = objNode.offsetWidth;
										if(ow > 0){
										  if(ow > 133 || ow <133){
											  now = parseInt((ow - 133)/2);
											}
										  allULs = objNode.getElementsByTagName('UL');
											if(allULs.length>0 && allULs[0]){
											  allULs[0].style.left = now+"px";
										  }
										}
							
										// change the stored active node to "link"
										//if(fom.activeNode!=null) if(fom.activeNode.getElementsByTagName('IMG').length>0) fom.activeNode.getElementsByTagName('IMG')[0].src = fom.activeNode.getElementsByTagName('IMG')[0].src.replace('_active','_link');
										// change the src of a child image
										imgNodes = objNode.getElementsByTagName('img');
										if(imgNodes.length>0) imgNodes[0].src = imgNodes[0].src.replace('_link','_hover');
										// hide all select form elements
										allSelects = document.getElementsByTagName('SELECT');
										if(document.all){
											for(var a=0; a<allSelects.length; a++){
												allSelects[a].style.visibility = 'hidden';
											}
										}
									}
								}
			this.remHover 	= 	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									var cmh = classBehaviour.classMouseHover;
									var fom = classBehaviour.foldOutMenu;
									// if no delay is required
									if(fom.delay==0){
										// emulate the parent node's mouseout event
										cmh.remHover(objNode);
										// change the stored active node to "active"
										if(fom.activeNode!=null){}
										// change the src of a child image
										imgNodes = objNode.getElementsByTagName('img');
										if(imgNodes.length>0) imgNodes[0].src = imgNodes[0].src.replace('_hover','_link');
										// restore all select form elements
										allSelects = document.getElementsByTagName('SELECT');
										if(document.all){
											for(var a=0; a<allSelects.length; a++){
												allSelects[a].style.visibility = 'visible';
											}
										}
									}else{
										// cancel the timeout on the delayed mouseevents
										clearTimeout(fom.timeout);
										// store the mouseout for delayed closing
										fom.foldIns[fom.foldIns.length] = objNode;
										// order a delayed handling of the saved up mouseouts
										fom.timeout = setTimeout('classBehaviour.foldOutMenu.addHover()',fom.delay);
									}
								}
		}
		// add this function to the classbehaviour object
		classBehaviour.foldOutMenu = new FoldOutMenu;
		classBehaviour.handlers[classBehaviour.handlers.length] = classBehaviour.foldOutMenu;
		
	  
	  //Class a link matching the document's url
		// define this class behaviour
		function MatchActiveUrl(){
			/* properties */
			this.name 		= 	'matchActiveUrl';
			/* methods */
			this.start		=	function(node){
									this.process(node);
								}
			this.convertAbsToRelUrls 	= 	function(strUrl){
												// is the url a relative path
												if(strUrl.indexOf('/')<0 || strUrl.substr(0,1)=='.' || strUrl.substr(0,1)=='/'){
													// the current absolute path
													strAbs = document.location.href;
													// remove the filename from the end
													strAbs = strAbs.substring(0, strAbs.lastIndexOf('/'));
													// while there are parent markers in the url
													while(strUrl.indexOf('../')==0){
														// remove one level from the absolute path
														strUrl = strUrl.replace('../','');
														// remove one parent marker from the relative path
														strAbs = strAbs.substring(0, strAbs.lastIndexOf('/'));
													}
													// remove all current dir markers from the relative url
													strUrl = strUrl.replace(/\.\//gi,'');
													// add the url to the absolute path
													strUrl = strAbs + '/' + strUrl;
												}
												return strUrl;
											}
			this.compareUrls 	= 	function(strUrlA,strUrlB){
										var intCurScore = 0;
										var intPotScore = 0;
										var intMaxScore = 0;
										var intA,intB;
										// replace most common illegal characters
										strUrlA = strUrlA.replace(/ /gi,"%20");
										strUrlB = strUrlB.replace(/ /gi,"%20");
										// remove anchors
										strUrlA = strUrlA.split('#')[0];
										strUrlB = strUrlB.split('#')[0];
										// make sure both paths are absolute
										strUrlA = this.convertAbsToRelUrls(strUrlA);
										strUrlB = this.convertAbsToRelUrls(strUrlB);
										// split the urls into manageable strings
										var arrUrlA = strUrlA.split(/[?&#\/]/i);
										var arrUrlB = strUrlB.split(/[?&#\/]/i);
										// for every string of UrlA
										for(intA=0; intA<arrUrlA.length; intA++){
											// is the string in the substrings of UrlB
											intB = 0; while(intB<arrUrlB.length && arrUrlA[intA]!=arrUrlB[intB]) intB += 1;
											// if a match was found, add length of string A to current score
											if(intB<arrUrlB.length) intCurScore += arrUrlA[intA].length;
											// add length of string A to potential score
											intPotScore += arrUrlA[intA].length;
										}
										// calcultate maximum score possible
										intMaxScore = strUrlB.length - arrUrlB.length + 1;
										// return the compare-score
										return intCurScore/intPotScore;
									}
			this.process 	= 	function(objNode){
									var cmh = classBehaviour.classMouseHover;
									var smh = classBehaviour.srcMouseHover;
									// get parent recursion
									var intToParent	= parseInt(classBehaviour.utilities.getClassParameter(objNode, 'toParent', '0'));
									// get parent href
									var intFromParent = parseInt(classBehaviour.utilities.getClassParameter(objNode, 'fromParent', '0'));
									// get the url and clean it up
									var strUrl = this.convertAbsToRelUrls(document.location.href);
									// get the href and clean it up
									var strHref = (intFromParent>0) ? this.convertAbsToRelUrls(objNode.parentNode.getAttribute('href')) : this.convertAbsToRelUrls(objNode.getAttribute('href')) ;
									// was the data bad
									if(strHref!=null){
										// compare score
										var ftlCompareScore = this.compareUrls(strUrl, strHref) * this.compareUrls(strHref, strUrl);
										// if the href matches the url 
										if(ftlCompareScore==1){
											// add the active class to the target item
											cmh.addActive(objNode);
											if(objNode.nodeName=='IMG') smh.addActive(objNode);
											// if a parent node also needs to be marked
											if(intToParent>0){
												// get the relevant parent node
												for(var intA=0; intA<intToParent; intA++) objNode = objNode.parentNode;
												// if the href matches the url
												if(ftlCompareScore==1){
													// add the active class to the parent item
													cmh.addActive(objNode);
												}
											}
											// report a match
											return true;
										}
									}
									// report no match
									return false;
								}
		}
		// add this function to the classbehaviour object
		classBehaviour.matchActiveUrl = new MatchActiveUrl;
		classBehaviour.handlers[classBehaviour.handlers.length] = classBehaviour.matchActiveUrl;
		
	  
	  	
		
/* TODO: controls for the scroller
	<li><a href="?3" id="prevControl"><img alt="Vorige" src="../images/movieGallery_previous_link.gif"/></a></li>
	<li><a href="?4" id="nextControl"><img alt="Volgende" src="../images/movieGallery_next_link.gif"/></a></li>
*/
		
		
		
	  // clear a form element filled with a help text
		// define this class behaviour
		function EmptyOnFocus(){
			/* properties */
			this.name 		= 	'emptyOnFocus';
			/* methods */
			this.start		=	function(node){
									node.onfocus = this.clear;
								}
			/* events */
			this.clear		=	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									// clear the contents if this is the first time
									if(objNode.className.indexOf('wasEmptied')<0){
										// clear this field
										objNode.value = '';
										// mark this input as cleared
										objNode.className += ' wasEmptied';
									}
								}
		}
		// add this function to the classbehaviour object
		classBehaviour.emptyOnFocus = new EmptyOnFocus;
		classBehaviour.handlers[classBehaviour.handlers.length] = classBehaviour.emptyOnFocus;
		
/* /TODO */
	// STARTUP-SEQUENCE
	// start the parsing of classes
	classBehaviour.parseDocument();

