
/*
name			: Class Behaviour
update			: 20050210
author			: Frank van Rooijen, Maurice van Creij
dependencies	: lib_classbehaviour.js
info			: /content/details.asp?id=20040805133501

1. Replace "link" in class
   class="classMouseHover link"
2. Replace "link" in src
   class="srcMouseHover"
3. Add display:none; on parse
   class="hideThisNode"
   class="showThisNode"
4. Add or remove display:none; onclick
   class="toggleNextNode[_CPno]"
   (Everything between [brackets] is optional. Leave out the [brackets] in the classname)
   CP = close the previous node, before opening a new one (default: no)   
5. Add display:none; to parent node
   class="closeParentNode"
6. Replace image with transparent version, invoke directX background loader
   class="pngAlpha"
7. Handle ondrag events
   class="dragAndDrop"
8. Open links in a popup
   class="openAsPopUp[_WI400][_HE300][_TByes][_SCyes][_RSyes][_STyes][_LOyes][_MEyes][_NMmyname]" 
   WI = width (default: automatic)
   HE = height (default: automatic)
   TB = toolbars (default: no)
   SC = Scrollbars (default: no)
   RS = Resizable (default: no)
   ST = Status bar (default: no)
   LO = Location bar (default: no)
   ME = Menus (default: no)
   NM = Window name (default: popup)
9. Shows the contents of a container as raw code
   class="showAsCode"
10. Chooses a random increment of an image source
   class="setRandomSrc[_MN0][_MX1]"
   MN = minimum (default: 0)
   MX = maximum (default: 1)
11. Class a link matching the document's url
   class="matchActiveUrl[_PR0]"
   PR = Apply class to parent (default: 0)
12 Add a className to a tag using the query parameter "class"
   class="addQueryToClassName"
13 Add a suffix to an image source using the query parameter "src"
   class="addQueryToSrc"
14 Validate the value of a for element to a predefined regular expression
   class="validateInput[_TY]"
   (Everything between [brackets] is optional. Leave out the [brackets] in the classname)
   TY = validator type (email,phone,dutchzipcode,date,memberid,money,bankaccount,alphanumeric,notempty,(isradiochecked))
   AE = allow empty (default: 0)
15 Triggers all validateInput class behaviours within a node after the onsubmit event.
   class="validateAllInput"
16 Resizes the window to avoid a scrollbar
   class="resizeToFit"
17 Alternates the classes of a table's rows and columns
   class="zebraTable"
18 Makes the headers of a table click/sortable
   class="sortTable"
19 Enforces minimal height of a container
   class="minHeight[_HE100pct][_OF100px]"
   HE = height + unit (default: no minimum. units: pct, px)
   OF = offset + unit (default: 0. units pct, px)
20 Enforces maximal width of a container
   class="maxWidth[_WI768px][_OF100px]"
   WI = width + unit (default: no minimum. units: pct, px)
   OF = offset + unit (default: 0. units pct, px)
21 Blinks
   class="blink"
22 Open a print dialog
   class="openAsPrintable"
*/

/* 
TODO: sizable table
*/


	// constants/configuration
		if(typeof(intDragAndDropGridWidth)=='undefined')	var intDragAndDropGridWidth = 16;
		if(typeof(intDragAndDropGridHeight)=='undefined')	var intDragAndDropGridHeight = 16;
		if(typeof(strTransparentImg)=='undefined')			var strTransparentImg = '_alpha.png';
	// primary functions - functionality
		// returns a string of parameters found in the classname which can be [eval]uated
		function getClassParameter(objNode,strClassNameHint){
			// get the className
			var arrClassNames = objNode.className.split(' '); var strClass; var intClass = 0;
			while(strClass==null && intClass<arrClassNames.length){
				strClass = (arrClassNames[intClass].toLowerCase().indexOf(strClassNameHint.toLowerCase())>-1) ? arrClassNames[intClass] : null;
				intClass += 1;
			}
			// get class parameters
			var evalParams = '';
			if(strClass!=null){
				var arrClassParams = strClass.split('_'); 
				for(var intParam=1; intParam<arrClassParams.length; intParam++){
					evalParams += "var " + arrClassParams[intParam].substr(0,2) + "= '" + arrClassParams[intParam].substr(2) + "';";
				}
			}
			return evalParams;
		}
		// return a parameter from the url's query strings
		function getQueryParameter(strParamName){
			// split the query string at the parameter name
			var arrQueryParameter = document.location.search.split(strParamName+"=");
			// split the parameter value from the rest of the string
			var strQueryParameter = (arrQueryParameter.length>1) ? arrQueryParameter[1].split("&")[0] : null ;
			// return the value
			return strQueryParameter;
		}
	// secondary function - constructors
		function parseForClasses(){
			var strClass, arrClass
			// get all elements
			var objAll = (document.all) ? document.all : document.getElementsByTagName("*");
			// for all elements
			for(var intA=0; intA<objAll.length; intA++){
				// get the element's class attribute
				strClass = objAll[intA].className;
				// if there is a class
				if(strClass!=null){	
					// split the class attribute into classes
					arrClass = strClass.split(' ');
					// for all sub-classes
					for(var intB=0; intB<arrClass.length; intB++){
						// choose known classes
						switch(arrClass[intB].toLowerCase()){
							// replace in class
							case "classmousehover" :
								objAll[intA].onmouseover = addClassHover;
								objAll[intA].onmouseout = remClassHover;
								objAll[intA].onclick = addClassActive;
								break;
							// replace in src sub-string
							case "srcmousehover" :
								cacheSrcHover(objAll[intA]);
								objAll[intA].onmouseover = addSrcHover;
								objAll[intA].onmouseout = remSrcHover;
								break;
							// add display='none'; on parse
							case "hidethisnode" : 
								/*event*/;
								objAll[intA].style.display = 'none';
								break;
							// affirms the visibility status in regard to toggles
							case "showthisnode" : 
								/*event*/;
								objAll[intA].style.display = 'block';
								objLastOpened = objAll[intA];
								break;
							// add display='none'; to parent node
							case "closeparentnode" : 
								/*event*/;
								objAll[intA].onclick = closeParentNode;
								break;
							// replace image with transparent version, invoke activeX background loader
							case "pngalpha" : 
								/*event*/;
								pngAlpha(objAll[intA]);
								objAll[intA].onload = pngAlpha;
								break;
							// handle ondrag events
							case "draganddrop" : 
								/*event*/;
								objAll[intA].onmousedown = dragPickUp;
								objAll[intA].onmouseup = dragDropDown;
								objAll[intA].onmousemove = dragMoveAway;
								// saved position
								dragRestore(objAll[intA]);
								break;
							// show contents as code
							case "showascode" : 
								/* event */
								showAsCode(objAll[intA]);
								break;
							// Add a className to a tag using the query parameter "class"',
							case "addquerytoclassname" :
								addQueryToClassName(objAll[intA]);
								break;
							// Add a suffix to an image source using the query parameter "src"',
							case "addquerytosrc" :
								addQueryToSrc(objAll[intA]);
								break;
							// Triggers all validateInput class behaviours within a node after the onsubmit event.',
							case "validateallinput" :
								objAll[intA].onsubmit = validateAllInput;
								break;
							// Resizes the window to avoid a scrollbar
							case "resizetofit" :
								objAll[intA].onload = resizeToFit;
								resizeToFit();
								break;
							// Alternates the classes of a table's rows and columns
							case "zebratable" :
								zebraTable(objAll[intA]);
								break;
							// Makes the headers of a table click/sortable
							case "sorttable" :
								objAll[intA].onclick = sortTable;
								break;
							// Blink
							case "blink" :
								blink(objAll[intA]);
								break;
							// Open print dialog
							case "openasprintable":
								objAll[intA].onclick = openAsPrintable;
								break;
							case "foldoutmenu":
								foldOutMenu.start(objAll[intA]);
								break;
							default :
								/*event*/;
						}
						// check for parsable classes
						if(arrClass[intB].toLowerCase().indexOf("openaspopup")>-1) objAll[intA].onclick = openAsPopup;
						if(arrClass[intB].toLowerCase().indexOf("setrandomsrc")>-1) setRandomSrc(objAll[intA]);
						if(arrClass[intB].toLowerCase().indexOf("togglenextnode")>-1) objAll[intA].onmouseover = toggleNextNode;
						if(arrClass[intB].toLowerCase().indexOf("matchactiveurl")>-1) matchActiveUrl(objAll[intA]);
						if(arrClass[intB].toLowerCase().indexOf("validateinput")>-1) objAll[intA].onblur = validateInput;
						if(arrClass[intB].toLowerCase().indexOf("minheight")>-1){
							// immediate event
							minHeight(objAll[intA]);
							// store object for acter resize
							arrHeightAdjusts[arrHeightAdjusts.length] = objAll[intA];
							window.onresize = setDimensions;
						}
						if(arrClass[intB].toLowerCase().indexOf("maxwidth")>-1){
							// immediate event
							maxWidth(objAll[intA]);
							// store object for acter resize
							arrWidthAdjusts[arrWidthAdjusts.length] = objAll[intA];
							window.onresize = setDimensions;
						}
					}
				}
			}
		}
	// ternary function - event handlers
		function openAsPrintable(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
// TODO: exchange stylesheet with print stylsheet
			// open the print dialog
			window.print();
		}
			// blink helpers
			function blinkLoop(intToBlink){
				objToBlink = arrBlink[intToBlink][0];
				objToBlink.className = (objToBlink.className.indexOf('blinkoff')>-1) ? objToBlink.className.replace('blinkoff','blinkon') : objToBlink.className.replace('blinkon','blinkoff');
				arrBlink[intToBlink][1] = setTimeout('blinkLoop('+intToBlink+')',arrBlink[intToBlink][2]);
			}
		var arrBlink = new Array();
		function blink(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// make new blink entry
			arrBlink[arrBlink.length] = new Array(objNode,null,1024);
			// start blink loop
			blinkLoop(arrBlink.length-1)
		}
			// resize helpers
			arrHeightAdjusts = new Array();
			arrWidthAdjusts = new Array();
			function setDimensions(){
				for(var intA=0; intA<arrHeightAdjusts.length; intA++){
					arrHeightAdjusts[intA].style.height = 'auto';
					minHeight(arrHeightAdjusts[intA]);
				}
				for(var intA=0; intA<arrWidthAdjusts.length; intA++){
					arrWidthAdjusts[intA].style.width = 'auto';
					maxWidth(arrWidthAdjusts[intA]);
				}
			}
		function minHeight(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// get the height parameters
			var strValidatorName, strMinHeight, strOffSet;		
			eval(getClassParameter(objNode,'minHeight'));
			strMinHeight	= (typeof(HE)!='undefined') ? HE : null ;
			strOffSet		= (typeof(OF)!='undefined') ? OF : null ;

			// get the current document and window dimensions
			var intDocHeight, intCanvasHeight;
			intDocHeight	= document.body.scrollHeight;
			intCanvasHeight	= (typeof(window.innerHeight)!='undefined') ? window.innerHeight : document.body.offsetHeight ;
			// adjust target container to fill the difference in dimensions
			var intMinHeight	= (strMinHeight.indexOf('pct')>-1) ? Math.round(intCanvasHeight * parseInt(strMinHeight) / 100) : parseInt(strMinHeight) ;
			var intOffSet		= (strOffSet.indexOf('pct')>-1) ? Math.round(intCanvasHeight * parseInt(strOffSet) / 100) : parseInt(strOffSet) ;
				// MSIE compensation
				if(document.all) intOffSet += 4;
			if(intDocHeight<intCanvasHeight) objNode.style.height = (intMinHeight - intOffSet) + 'px';
		}
		function maxWidth(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// get the width parameters
			var strValidatorName, strMaxWidth, strOffSet;		
			eval(getClassParameter(objNode,'maxWidth'));
			strMaxWidth		= (typeof(WI)!='undefined') ? WI : null ;
			strOffSet		= (typeof(OF)!='undefined') ? OF : null ;
			// get the current document and window dimensions
			var intDocWidth, intCanvasWidth;
			intDocWidth		= document.body.scrollWidth;
			intCanvasWidth	= (typeof(window.innerWidth)!='undefined') ? window.innerWidth : document.body.offsetWidth ;
			// adjust target container to fill the difference in dimensions
			var intMaxWidth	= (strMaxWidth.indexOf('pct')>-1) ? Math.round(intCanvasWidth * parseInt(strMaxWidth) / 100) : parseInt(strMaxWidth) ;
			var intOffSet	= (strOffSet.indexOf('pct')>-1) ? Math.round(intCanvasWidth * parseInt(strOffSet) / 100) : parseInt(strOffSet) ;
			if(intDocWidth>intMaxWidth) objNode.style.width = (intMaxWidth - intOffSet) + 'px';
		}
			// sort helpers
			function sortNodesForward(objA,objB){
				var intCompare = 0;
				// get the string values from the nodes
				strA = (objA.getElementsByTagName(strSortColTag)[intSortCol].childNodes.length == 0) ? ' ' : objA.getElementsByTagName(strSortColTag)[intSortCol].innerHTML;
				strB = (objB.getElementsByTagName(strSortColTag)[intSortCol].childNodes.length == 0) ? ' ' : objB.getElementsByTagName(strSortColTag)[intSortCol].innerHTML;
				// get the numeric values from the nodes
				intA = parseInt(strA);
				intB = parseInt(strB);
				// compare the values for the sort funtion
				if(strA==strB || (navigator.appVersion.indexOf('MSIE 5.0')>-1)){
					// equal
					return 0;
				}else if(isNaN(intA) || isNaN(intB)){
					// remove any tags that might be in the way
					var regTags = new RegExp('<(.|\n)+?>','gi');
					strA = strA.replace(regTags,'');
					strB = strB.replace(regTags,'');
					// compare the textual values
					return (strA>strB) ? 1 : -1 ;
				}else{
					// compare the numeric values
					return intB - intA;
				}
			}
			function sortNodesReverse(objA,objB){
				var intCompare = 0;
				// get the string values from the nodes
				strA = (objA.getElementsByTagName(strSortColTag)[intSortCol].childNodes.length == 0) ? ' ' : objA.getElementsByTagName(strSortColTag)[intSortCol].innerHTML;
				strB = (objB.getElementsByTagName(strSortColTag)[intSortCol].childNodes.length == 0) ? ' ' : objB.getElementsByTagName(strSortColTag)[intSortCol].innerHTML;
				// get the numeric values from the nodes
				intA = parseInt(strA);
				intB = parseInt(strB);
				// compare the values for the sort funtion
				if(strA==strB || (navigator.appVersion.indexOf('MSIE 5.0')>-1)){
					// equal
					return 0;
				}else if(isNaN(intA) || isNaN(intB)){
					// remove any tags that might be in the way
					var regTags = new RegExp('<(.|\n)+?>','gi');
					strA = strA.replace(regTags,'');
					strB = strB.replace(regTags,'');
					// compare the textual values
					return (strA<strB) ? 1 : -1 ;
				}else{
					// compare the numeric values
					return intA - intB;
				}
			}
		var intSortCol, strSortRowTag, strSortColTag;
		function sortTable(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// defaults
			intSortCol		= 0;
			strSortRowTag	= 'TR';
			strSortColTag	= 'TD';
			strSortDir		= 'forwardSorted';
			// Find column number
			var objSiblings	= objNode.parentNode.getElementsByTagName(objNode.nodeName);
			for(var intA=0; intA<objSiblings.length; intA++){
				// test if this is the clicked node
				if(objSiblings[intA] == objNode){
					intSortCol = intA;
					strSortDir = (objSiblings[intA].className.indexOf('forwardSorted')>-1) ? 'reverseSorted' : 'forwardSorted' ;
				}
				// unmark any previously sorted column
				objSiblings[intA].className = objSiblings[intA].className.replace('forwardSorted','');
				objSiblings[intA].className = objSiblings[intA].className.replace('reverseSorted','');
			}
			// make a nodelist
			var objNodeTable	= (objNode.parentNode.parentNode.nodeName != 'TABLE') ? objNode.parentNode.parentNode.parentNode : objNode.parentNode.parentNode ;
			var objNodeRoot		= objNode.parentNode.parentNode ;
			var objNodeList		= objNodeRoot.getElementsByTagName(strSortRowTag);
			var arrNodeList		= new Array();
			// for all table rows (except the header)
			for(var intA=0; intA<objNodeList.length; intA++){
				if(objNodeList[intA].getElementsByTagName('TH').length<1){
					arrNodeList[arrNodeList.length] = objNodeList[intA];
				}
			}
			// sort the collection using a helper function
			arrNodeList = (strSortDir=='forwardSorted') ? arrNodeList.sort(sortNodesForward) : arrNodeList.sort(sortNodesReverse);
			// clear the unsorted nodelist
			for(var intA=0; intA<objNodeList.length; intA++){
				if(objNodeList[intA].getElementsByTagName('TH').length<1){
					objNodeRoot.removeChild(objNodeList[intA]);
				}
			}
			// append the sorted nodelist
			for(var intA=0; intA<arrNodeList.length; intA++){
				objNodeRoot.appendChild(arrNodeList[intA]);
			}
			// reapply the zebra effect
			if(objNodeTable.className.toLowerCase().indexOf('zebratable')>-1) zebraTable(objNodeTable);
			// mark the newly sorted column
			objNode.className += ' ' + strSortDir;
		}
		function zebraTable(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			var objRows, objCols, intCellNumber;
			// get all table rows
			objRows = objNode.getElementsByTagName('TR');
			// for all table rows
			for(var intRow=0; intRow<objRows.length; intRow++){
				// undo any previous classing
				objRows[intRow].className = objRows[intRow].className.replace('odd','');
				objRows[intRow].className = objRows[intRow].className.replace('even','');
				// add oddrow or evenrow class to the row
				objRows[intRow].className += (intRow%2==0) ? ' odd' : ' even' ;
				// and row and col counters if they're not allready present
				if(objRows[intRow].className.indexOf('row_')<0) objRows[intRow].className += ' row_' + intRow;
				// get all nodes in this row
				objCols = objRows[intRow].childNodes;
				// for every node in the row
				intCellNumber = 0;
				for(var intCol=0; intCol<objCols.length; intCol++){
					// is this a cell or a header
					if(objCols[intCol].nodeName.indexOf('text')<0){
						// undo any previous classing
						objCols[intCol].className = objCols[intCol].className.replace('odd','');
						objCols[intCol].className = objCols[intCol].className.replace('even','');
						// add oddcol or evencol class
						objCols[intCol].className += (intCellNumber%2==0) ? ' odd' : ' even' ;
						// and row and col counters if they're not allready present
						if( objCols[intCol].className.indexOf('col_')<0) objCols[intCol].className += ' col_' + intCellNumber;
						// keep cell numbers
						intCellNumber += 1;
					}
				}
			}
		}
		function resizeToFit(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			var intScrollX, intScrollY, intWinWidth, intWinHeight, intMaxWidth, intMaxHeight;
			// start dimensions
			intMaxWidth = screen.availWidth;
			intMaxHeight = screen.availHeight;
			// while the scroll position is not 0
			var intWhileCount = 0;
			do{
				// scroll the document by 1 pixel
				window.scrollTo(1,1);
				// measure the scroll position			
				intScrollX = (document.all) ?  document.body.scrollLeft : window.pageXOffset ;
				intScrollY = (document.all) ? document.body.scrollTop : window.pageYOffset ;
				// measure window size
				intWinWidth = (document.all) ? document.body.offsetWidth : window.innerWidth ;
				intWinHeight = (document.all) ? document.body.offsetHeight : window.innerHeight ;
				// if the scroll position is not 0
				if(intScrollX>0){
					// make the window larger
					window.resizeBy(32,0)
				}
				if(intScrollY>0){
					// make the window larger
					window.resizeBy(0,32)
				}
				// count the steps
				intWhileCount += 1;
			}while((intScrollX>0 || intScrollY>0) && intWinWidth<intMaxWidth && intWinHeight<intMaxHeight && intWhileCount<32);
		}
		function validateAllInput(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			var booPassed = true;
			// get all subnodes
			var objSubNodes = (objNode.all) ? objNode.all : objNode.getElementsByTagName("*") ;
			// for all nodes
			for(var intA=0; intA<objSubNodes.length; intA++){
				// Does this node have the validateInput put class? Invoke the validator function upon it.
				if(objSubNodes[intA].className.toLowerCase().indexOf('validateinput')>-1) booPassed = (validateInput(objSubNodes[intA]) && booPassed);
			}
			// is the form valid enough?
			return booPassed;
		}
			// custom validation functions
			function validate_bankaccount(objNode){
				var intDeel, intRest;
				var strInput = objNode.value;
				var intTot=0;
				if (strInput.length!=9){
					return false;
				}else{
					for (i=0; i<strInput.length; i++) intTot += strInput.substr(i,1) * (9 - i);
					intDeel = intTot/11;
					intRest = intTot%11;
					return (intRest==0);
				}
			}
		function validateInput(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// default validator values
			var booEmptyTest,booBooValidator,booRegExpValidator, regExpValidator,booFuncValidator;
			// get the warning message
			var objWarningNode = (objNode.nextSibling.nodeName.indexOf("text")<0) ? objNode.nextSibling : objNode.nextSibling.nextSibling ;
			// get the type of validation required			
			eval(getClassParameter(objNode,'validateInput'));
			strValidatorName	= (typeof(TY)!='undefined') ? TY : '' ;
			intAllowEmpty		= (typeof(AE)!='undefined') ? parseInt(AE) : 0 ;
			// empty test
			booEmptyTest = (intAllowEmpty==1 && objNode.value=='') ? true : false ;
			// boolean test
			switch(strValidatorName){
// TODO: need to check all radio buttons in one family
				case 'isradiochecked' : 
					booBooValidator = true;
				default :
					booBooValidator = true;
			}
			// regular expression test
			if(navigator.appVersion.indexOf('MSIE 5.0')>-1 && strValidatorName=='money') strValidatorName = null;
			switch(strValidatorName){
				case 'email' : 
					booRegExpValidator = (objNode.value.match(/^[\w\.\-\,\+]+@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/)!=null);
					break;
				case 'phone' : 
					booRegExpValidator = (objNode.value.match(/(^\+[0-9]{2}|^\+[0-9]{2}\(0\)|^\(\+[0-9]{2}\)\(0\)|^00[0-9]{2}|^0)([0-9]{9}$|[0-9\-\s]{10}$)/)!=null); 
					break;
				case 'dutchzipcode' : 
					booRegExpValidator = (objNode.value.match(/^[0-9]{4}\s{0,1}[a-zA-Z]{2}$/)!=null); 
					break;
				case 'date' : 
					booRegExpValidator = (objNode.value.match(/^\d{4}\-\d{1,2}\-\d{1,2}$/)!=null);
					break;
				case 'memberid' : 
					booRegExpValidator = (objNode.value.match(/^[0-9]+$/)!=null); 
					break;
				case 'money' :
					regExpValidator = new RegExp('^(?!\u00a2)(?!0,?\d)(\d{1,3}(\.\d{3})*|(\d+))(\,\d{2})?$');
					booRegExpValidator = (objNode.value.match(regExpValidator)!=null);
					break;
				case 'alphanumeric' :
					booRegExpValidator = (objNode.value.match(/^[a-zA-Z0-9]/)!=null);
				default :
					booRegExpValidator = true;
			}
			// custom test
			switch(strValidatorName){
				case 'bankaccount' :
					booFuncValidator = validate_bankaccount(objNode);
					break;
				case 'notempty' :
					booFuncValidator = (objNode.value!="");
					break;
				default :
					booFuncValidator = true;
			}
			// close the error message by default
			objWarningNode.style.display = 'none';
			// show or hide the warning message based on the validator's match
			if(!booEmptyTest)objWarningNode.style.display = (booBooValidator && booRegExpValidator && booFuncValidator) ? 'none' : 'block' ;
			// return a pass of fail boolean to whoever may want to know the results of the test
			return !(objWarningNode.style.display=='block');
		}
		function addQueryToClassName(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// get the query parameter
			var strQueryParameter = getQueryParameter("class");
			// add to front of classNames
			if(strQueryParameter!=null) objNode.className = strQueryParameter + ' ' + objNode.className;
		}
		function addQueryToSrc(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// get the query parameter
			var strQueryParameter = getQueryParameter("src")
			// add to front of classNames
			if(strQueryParameter!=null) objNode.src = objNode.src.replace('.','_'+strQueryParameter+'.');
		}
		var objPopup;
		function openAsPopup(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// get the parameters from the classname
			eval(getClassParameter(objNode,'openAsPopUp'));
			// get width parameter
			var strWidth		= (typeof(WI)!='undefined') ? 'width='+WI : '' ;
			// get height parameter
			var strHeight		= (typeof(HE)!='undefined') ? ',height='+HE : '' ;
			// get toolbar string
			var strToolbars		= (typeof(TB)!='undefined') ? ',toolbar='+TB : ',toolbar=no' ;
			// get scrolling string
			var strScrolling	= (typeof(SC)!='undefined') ? ',scrollbars='+SC : ',scrollbars=no' ;
			// get status string
			var strStatus		= (typeof(ST)!='undefined') ? ',status='+ST : ',status=no' ;
			// get resizable string = 
			var strResize		= (typeof(RS)!='undefined') ? ',resizable='+RS : ',resizable=no' ;
			// get resizable string = 
			var strLocation		= (typeof(LO)!='undefined') ? ',location='+LO : ',location=no' ;
			// get resizable string = 
			var strMenu			= (typeof(ME)!='undefined') ? ',menu='+ME : ',menu=no' ;
			// window name
			var strName			= (typeof(NM)!='undefined') ? NM : 'popup' ;
			// open requested window
			objPopup = window.open(objNode.getAttribute('href'), strName, strWidth+strHeight+strScrolling+strToolbars+strStatus+strResize+strLocation+strMenu);
			objPopup.focus();
			// cancel click
			return false;
		}
		function setRandomSrc(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// get the parameters from the classname
			eval(getClassParameter(objNode,'setRandomSrc'));
			// get min parameter
			var intMin		= (typeof(MN)!='undefined') ? parseInt(MN) : 0 ;
			// get max parameter
			var intMax		= (typeof(MX)!='undefined') ? parseInt(MX) : 1 ;
			// generate random number
			var intRandom = Math.round(Math.random()*(intMax-intMin))+intMin;
			// replace default increment by random number
			objNode.src = objNode.src.replace('_0','_'+intRandom);
		}
		function addClassHover(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// replace link by hover
			objNode.className = objNode.className.replace('link','hover');
		}
		function remClassHover(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// replace hover by link
			objNode.className = objNode.className.replace('hover','link');
		}
		function addClassActive(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// replace link by active
			objNode.className = objNode.className.replace('link','active');
			// replace hover by active
			objNode.className = objNode.className.replace('hover','active');
		}
			// helper functions for srcMouseHover
			var arrCachedImages = new Array();
			function cacheSrcHover(that) {
				var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
				// replace link by hover
				var intCachedImageIdx = arrCachedImages.length;
				// hover version
				arrCachedImages[intCachedImageIdx] = new Image();
				arrCachedImages[intCachedImageIdx].src = objNode.src.replace('link','hover');
				// active version
				arrCachedImages[intCachedImageIdx+1] = new Image();
				arrCachedImages[intCachedImageIdx+1].src = objNode.src.replace('link','active');
			}
		function addSrcHover(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// replace link by hover
			objNode.src = objNode.src.replace('link','hover');
		}
		function remSrcHover(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// replace link by hover
			objNode.src = objNode.src.replace('hover','link');
		}
			// helper function for 
			var objLastOpened;
			function toggleThisNode(that, strClosePrevious){		
				var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
				// restore previous node
				if(objLastOpened!=null && objLastOpened!=objNode && strClosePrevious=='yes') objLastOpened.style.display = 'none';
				// toggle node's visibility
				objNode.style.display = (objNode.style.display=='none') ? 'block' : 'none' ;
				// remember last node
				objLastOpened = objNode;	
			}
		var objLastToggled;
		function toggleNextNode(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// determine the next node
			var objNextNode = (objNode.nextSibling.nextSibling.nodeName.indexOf("text")<0) ? objNode.nextSibling.nextSibling : objNode.nextSibling.nextSibling.nextSibling ;
			// get the parameters from the classname
			eval(getClassParameter(objNode,'toggleNextNode'));
			// get parent recursion
			var strClosePrevious = (typeof(CP)!='undefined') ? CP : 'no' ;
			// If the amount of a href tags in the next node is more than one
			if ( objNextNode.getElementsByTagName('a').length > 1 )
			{
				// toggle it's visibility
				toggleThisNode(objNextNode, strClosePrevious);				
				// If the next node has been hidden
				if(objNextNode.style.display=='none'){
					// restore current node's click state
					objNode.className = objNode.className.replace('active','link');
					if(objNode.src!=null) objNode.src = objNode.src.replace('active','link');
				}else{
					// mark current node as active
					objNode.className = objNode.className.replace('link','active');
					objNode.className = objNode.className.replace('hover','active');
					if(objNode.src!=null) objNode.src = objNode.src.replace('link','active');
					if(objNode.src!=null) objNode.src = objNode.src.replace('hover','active');
					// restore previous node's click state
					if(objLastToggled!=null && objLastToggled!=objNode){
						objLastToggled.className = objLastToggled.className.replace('active','link');
						if(objLastToggled.src!=null) objLastToggled.src = objLastToggled.src.replace('active','link');
					}
				}
				// remember last node
				objLastToggled = objNode;
				// cancel onclick event
				return false;
			}else{
				return true;
			}
		}
		function closeParentNode(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// hide the parent node
			objNode.parentNode.style.display = 'none';
			// restore previous node's click state
			if(objLastToggled!=null && objLastToggled!=objNode) objLastToggled.className = objLastToggled.className.replace('active','link');
			// cancel onclick event
			return false;
		}
		function pngAlpha(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// if the image has been processed before
			if(objNode.src.indexOf('_alpha')<0){
				// alpha image url
				var strAlphaSrc = objNode.src.replace(/.png|.jpg|.gif/gi,"_alpha.png")
				// for the downloeve browser MSIE
				if(typeof(objNode.style.filter)!='undefined'){
					// change the image styles
					objNode.style.width		= objNode.width + 'px';
					objNode.style.height	= objNode.height + 'px';
					objNode.style.filter	= "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + strAlphaSrc + "')";
					// get the path to the image folder
					var strPathSrc = '';
					var arrPathSrc = objNode.src.split('/');
					for(var intA=0; intA<arrPathSrc.length-1; intA++){strPathSrc += arrPathSrc[intA] + '/'};
					// replace the original with the alpha variant
					objNode.src = strPathSrc + strTransparentImg;
				// for the rest of the world
				}else{
					// replace the image source with the alpha channel version
					objNode.src = strAlphaSrc;
				}
			}
		}
			// drag and drop helper functions
			function dragRestore(that){
				var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
				// is lib_cookies available
				if(typeof(setCookie)!='undefined'){
					var strStyles, arrStyles;
					// retrieve styles string
					strStyles = getCookie('dragposition');					
					// were any styles recovered
					if(strStyles!=null){
						arrStyles = strStyles.split(',');
						// does the stored positions match the object
						if(arrStyles[0]==objNode.id && arrStyles.length>2){
							objNode.style.left = arrStyles[1];
							objNode.style.top = arrStyles[2];
						}
					}
				}
			}
			function dragStore(that){
				var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
				// is lib_cookies available
				if(typeof(setCookie)!='undefined'){
					// store styles
					setCookie('dragposition', objNode.id + ',' + objNode.style.left + ',' + objNode.style.top, null, '/', null, null);
				}
			}
		var intPickupX, intPickupY, intPickupZ;
		function dragPickUp(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// store pickup location
			intPickupX = (document.all) ? event.x : that.layerX ;
			intPickupY = (document.all) ? event.y : that.layerY ;
			intPickupZ = objNode.style.zIndex;
			// default starting position if none was given
			if(objNode.style.position!='absolute') objNode.style.position = 'absolute';
			if(objNode.style.left=='') objNode.style.left = 0 + 'px'; 
			if(objNode.style.top=='') objNode.style.top = 0 + 'px';
			// promote z position
			objNode.style.zIndex = 1024;
			// cancel browser mouse handler
			return false;
		}
		var intGridX=intDragAndDropGridWidth; var intGridY=intDragAndDropGridHeight;
		function dragDropDown(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// snap coordinates to grid
			if(intGridX>0) objNode.style.left = Math.round(parseInt(objNode.style.left)/intGridX)*intGridX + "px";
			if(intGridY>0) objNode.style.top = Math.round(parseInt(objNode.style.top)/intGridY)*intGridY + "px";
			// restore z position
			objNode.style.zIndex = intPickupZ;
			// clear pickup location
			intPickupX = null;
			intPickupY = null;
			intPickupZ = null;
			// store the position in a cookie
			dragStore(objNode);
			// cancel browser mouse handler
			return false;
		}
		function dragMoveAway(that){
			if(intPickupZ!=null){
				var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
				// mouse position
				intEventX = (document.all) ? event.x : that.layerX ;
				intEventY = (document.all) ? event.y : that.layerY ;
				// current object position
				var intStyleX = (objNode.style.left.indexOf('px')<0) ? 0 : parseInt(objNode.style.left) ;
				var intStyleY = (objNode.style.top.indexOf('px')<0) ? 0 : parseInt(objNode.style.top) ;
				// new object position
				if(intPickupX!=null) objNode.style.left = (intStyleX+intEventX-intPickupX) + 'px';
				if(intPickupY!=null) objNode.style.top = (intStyleY+intEventY-intPickupY) + 'px';
				// update pickup location (for some browsers)
				if(document.all) intPickupX = intEventX;
				if(document.all) intPickupY = intEventY;
				// cancel browser mouse handler
				return false;
			}
		}
		function showAsCode(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// replace html tags
			objNode.innerHTML = objNode.innerHTML.replace(/</gi,"&lt;").replace(/>/gi,"&gt;\n").replace(/ /gi,"&nbsp;").replace(/\t/gi,"&nbsp;&nbsp;&nbsp;").replace(/\n/gi,"<br />");
		}
			// helper functions for matchActiveUrl()
			function convertAbsToRelUrls(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;
			}
			function compareUrls(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 = convertAbsToRelUrls(strUrlA);
				strUrlB = 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;
			}
		function matchActiveUrl(that){
			var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
			// get the parameters from the classname
			eval(getClassParameter(objNode,'matchActiveUrl'));
			// get parent recursion
			var intToParent	= (typeof(PR)!='undefined') ? parseInt(PR) : 0 ;
			// get the url and clean it up
			var strUrl = convertAbsToRelUrls(document.location.href);
			// get the href and clean it up
			var strHref = convertAbsToRelUrls(objNode.getAttribute('href'));
			// was the data bad
			if(strHref!=null){
				// compare score
				var ftlCompareScore = compareUrls(strUrl, strHref) * compareUrls(strHref, strUrl);
				// if the href matches the url 
				if(ftlCompareScore==1){
					// add the active class to the target item
					addClassActive(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, add the active class to the parent item
						if(ftlCompareScore==1) addClassActive(objNode);
					}
				}
			}
		}
	// events
	
		// 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 = classMouseHover;
									// replace link by hover
									objNode.className = (cmh.hasNoStateClass(objNode)) ? 'hover ' + objNode.className : objNode.className.replace('link','hover') ;
								}
			this.remHover 	= 	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									var cmh = classMouseHover;
									// replace hover by link
									objNode.className = (cmh.hasNoStateClass(objNode)) ? 'link ' + objNode.className : objNode.className.replace('hover','link') ;
								}
			this.addActive 	= 	function(that){
									var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
									var cmh = 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
		classMouseHover = new ClassMouseHover;

	
		// Make foldout menu
		// define this class behaviour
		function FoldOutMenu(){
			/* properties */
			this.name 		= 	'foldOutMenu';
			this.timeout	=	null;
			this.delay		=	1024;
			this.foldIns 	= 	new Array();
			this.activeNode =	null;
			/* methods */
			this.start		=	function(node){
									this.process(node);
								}
			this.process 	= 	function(objNode){
									var objNodes, objMatch;
									//var mau = 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 = 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 = classMouseHover;
									var fom = 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) 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[0].src.indexOf('_active') == -1){	
								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);
										// change the stored active node to "link"
										if(fom.activeNode!=null) 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 = classMouseHover;
									var fom = 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('foldOutMenu.addHover()',fom.delay);
									}
								}
		}
		
		// add this function to the classbehaviour object
		foldOutMenu = new FoldOutMenu;
			
		//onload = parseForClasses;
		parseForClasses();
