/*
	name			: ClassBehaviours, the javascript framework based on class-name parsing
	update			: 9.3.17
	author			: Maurice van Creij, Marc Molenwijk
	dependencies	: jquery.classbehaviours.js
	info			: http://www.classbehaviours.com/

    This file is part of jQuery.classBehaviours.

    ClassBehaviours is a javascript framework based on class-name parsing.
    Copyright (C) 2008  Maurice van Creij

    ClassBehaviours is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ClassBehaviours is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ClassBehaviours. If not, see http://www.gnu.org/licenses/gpl.html.
*/

	// create the jQuery object if it doesn't already exist
	if(typeof(jQuery)=='undefined') jQuery = function(){};

	// create the root classbehaviours object if it doesn't already exist
	if(typeof(jQuery.classBehaviours)=='undefined') jQuery.classBehaviours = function(){};

	// create the handlers child object if it doesn't already exist
	if(typeof(jQuery.classBehaviours.handlers)=='undefined') jQuery.classBehaviours.handlers = function(){}

	// AJAX interface
	jQuery.classBehaviours.ajax = {
	    // properties
	    queue: new Array(),
	    // utilities
	    getNodeValue: function(node) {
	        // get the text value from a node if there is one
	        strValue = (node.childNodes.length > 0) ? node.firstChild.nodeValue : null;
	        // return it as text or a number
	        return (isNaN(strValue) || strValue == null) ? strValue : parseInt(strValue);
	    },
	    setNodeValue: function(node, strValue) {
	        // if this node already has a textNode
	        if (node.childNodes.length > 0) {
	            // set the value of the existing textNode
	            node.firstChild.nodeValue = strValue;
	        } else {
	            // or make a new textNode
	            var objNewNode = ajax.createTextNode(strValue);
	            node.appendChild(objNewNode);
	        }
	    },
	    getChildNumber: function(node) {
	        var nodes = node.parentNode.childNodes;
	        // look for the matching node in a list of childNodes
	        var intTextNodes = 0;
	        for (var intA = 0; intA < nodes.length; intA++) {
	            // count the amount of text nodes
	            if (nodes[intA].nodeName == '#text') intTextNodes += 1;
	            // return the found node and substract the amount of text nodes
	            if (node == nodes[intA]) return intA - intTextNodes;
	        }
	        // return null if no match was found
	        return null;
	    },
	    serialize: function(node, inner) {
	        // if the standard method is valid
	        if (typeof (XMLSerializer) != 'undefined') {
	            nodeXml = (new XMLSerializer()).serializeToString(node);
	        }
	        // if this is MSIE and XML was given
	        else if (node.xml != null) {
	            nodeXml = node.xml;
	        }
	        // if this is MSIE and a whole document was given
	        else if (node.getElementsByTagName('HTML').length > 0) {
	            nodeXml = '<!DOCTYPE html><HTML>' + node.getElementsByTagName('HTML')[0].innerHTML + '</HTML>';
	        }
	        // if this is MSIE and an HTML4 fragment was given
	        else if (node.innerHTML != null && node.innerHTML != '') {
	            nodeXml = '<' + node.nodeName + ' class="' + node.className + '" id="' + node.id + '">' + node.innerHTML + '</' + node.nodeName + '>';
	        }
	        // if this is MSIE and an HTML5 fragment was given
	        else {
	            // pick parent nodes until a node is found for which the innerHTML actually works
	            nodeXml = '';
	            nodeRoot = node;
	            while (nodeRoot != nodeRoot.parentNode && nodeXml == '') {
	                // try the next parent
	                nodeRoot = nodeRoot.parentNode;
	                // get the innerHTML of that
	                nodeXml = nodeRoot.innerHTML;
	            }
	            // to find the tag name of the string, look for the id in the string
	            idIndex = nodeXml.indexOf('id="' + node.id + '"');
	            if (idIndex < 0) idIndex = nodeXml.indexOf('id=' + node.id + ' ');
	            if (idIndex < 0) idIndex = nodeXml.indexOf('id=' + node.id + '>');
	            // split the string before this position
	            tagName = nodeXml.substring(0, idIndex);
	            // find the last < and split it after
	            openTagIndex = tagName.lastIndexOf('<');
	            tagName = tagName.substring(openTagIndex + 1);
	            // find the first ' ' and split it before
	            tagName = tagName.substring(0, tagName.lastIndexOf(' '));
	            // split the string after the id position
	            endTags = nodeXml.substring(idIndex);
	            // while the close tag was not found and the string did not end
	            charCount = openTagIndex + 1;
	            tagCount = 1;
	            tries = 0;
	            while (tagCount > 0 && tries < 5) {
	                // look forward through the string to count similar tags
	                nextStart = nodeXml.indexOf('<' + tagName, charCount);
	                if (nextStart == -1) nextStart = 1000000;
	                nextClose = nodeXml.indexOf('</' + tagName + '>', charCount);
	                if (nextClose == -1) nextClose = 1000000;
	                // if you find a similar opening tag add 1 to the count
	                if (nextStart < nextClose) {
	                    tagCount += 1;
	                    charCount = nextStart + 1;
	                }
	                // if you find a similar closing tag substract 1 from the count
	                if (nextClose < nextStart) {
	                    tagCount -= 1;
	                    charCount = nextClose + 1;
	                }
	                // if the count reaches 0 the proper closing tag was found
	                tries += 1;
	            }
	            // cut off the rest of the string using the character counter
	            closeTagIndex = nextClose + tagName.length + 3;
	            // return the resulting clip
	            nodeXml = nodeXml.substring(openTagIndex, closeTagIndex);
	        }
	        // remove the outer tag if required
	        if (inner) {
	            // split after first > and before last <
	            nodeXml = nodeXml.substring(nodeXml.indexOf('>') + 1, nodeXml.lastIndexOf('<'));
	        }
	        // give the extracted element back
	        return nodeXml;
	    },
	    deserialize: function(text) {
	        newElement = document.createElement('DIV');
	        newElement.innerHTML = text;
	        return newElement;
	    },
	    defaultProgress: function(progressStatus, progressNode, progressError) {
	        // fill the refered node with a progress report
	        progressNode.innerHTML = (progressStatus > -1) ? 'Loading: ' + Math.round(progressStatus * 100) + '%' : 'Error: ' + progressError;
	    },
	    defaultLoad: function(loadXml, loadNode, loadText) {
	        // fill the refered node with the returned html string
	        loadNode.innerHTML = loadText.split('<body>')[1].split('</body>')[0];
	    },
	    getPostValues: function(node, nodeId) {
	        // use the id if needed
	        if (nodeId != null) node = document.getElementById(nodeId);
	        // get all elements of this form
	        formNode = jQuery.classBehaviours.utilities.rootNode(node, 'FORM');
	        rootNode = jQuery.classBehaviours.utilities.rootNode(node, 'FIELDSET');
	        allNodes = rootNode.getElementsByTagName('*');
	        postValues = '';
	        // see if we can do anything to make it easier for .NET
	        viewStateTarget = document.getElementById('__EVENTTARGET');
	        viewStateArgument = document.getElementById('__EVENTARGUMENT');
	        //  .NET replaces the last _ with a $ for eventtarget validation
	        eventTarget = '';
	        split = node.id.split("_");
	        if (split.length > 1) {
	            for (var a = 0; a < split.length; a++)
	                if (split[a] != '')
	                eventTarget += (a == split.length - 1 ? '$' + split[a] : '_' + split[a]);
	        }
	        else eventTarget = node.id;
	        if (viewStateTarget != null) viewStateTarget.value = eventTarget; // REMOVED BY MARC
	        else postValues = '__EVENTTARGET=' + eventTarget;
	        if (viewStateArgument != null && node.value != null) viewStateArgument.value = node.value; // REMOVED BY MARC
	        // for all elements
	        for (var a = 0; a < allNodes.length; a++) {
	            // build the query string from the name and value pairs
	            if (
					allNodes[a].nodeName == 'INPUT' ||
					allNodes[a].nodeName == 'SELECT' ||
					allNodes[a].nodeName == 'TEXTAREA'
				)
	                if (
						allNodes[a].getAttribute('type') != 'submit' &&
						allNodes[a].getAttribute('type') != 'image' &&
						allNodes[a].getAttribute('type') != 'button' &&
						allNodes[a].getAttribute('type') != 'reset'
					)
	            //  Add all exception the viewstate
	                if (allNodes[a].name != '__VIEWSTATE') // ADD BY MARC
	                postValues += (allNodes[a].checked || (allNodes[a].type != 'radio' && allNodes[a].type != 'checkbox')) ?
							    allNodes[a].name + '=' + escape(allNodes[a].value) + '&' :
							    '';
	        }
	        // add the value of the pressed button
	        if (node.nodeName == 'BUTTON' || node.getAttribute('type') == 'submit' || node.getAttribute('type') == 'button') postValues += (node.name == null ? node.id : node.name) + '=' + node.value + '&'; //  ALTERED BY MARC
	        // add the cookie value too
	        if (document.cookie != null) postValues += document.cookie.replace(/; /gi, "&"); // +'&';
	        // let the back-end know this is an AJAX thing
	        //postValues += 'ajax=1';
	        // return the values
	        return postValues;
	    },
	    // methods
	    addRequest: function(url, loadHandler, progressHandler, post, referingObject) {
	        // get the first free slot in the que
	        index = this.queue.length;
	        // add new request to the end of the que
	        this.queue[index] = new this.HttpRequest();
	        // set request constants
	        this.queue[index].idx = index;
	        this.queue[index].url = url;
	        this.queue[index].post = post;
	        this.queue[index].method = (post != null) ? 'POST' : 'GET';
	        // request events
	        this.queue[index].doOnLoad = loadHandler;
	        this.queue[index].doOnProgress = progressHandler;
	        this.queue[index].referObject = referingObject;
	        // ask the queue handler to handle the next queued item
	        this.handleQueue();
	        // return false
	        return false;
	    },
	    makeRequest: function(queued) {
	        // branch for native XMLHttpRequest object
	        if (window.XMLHttpRequest) {
	            queued.request = new XMLHttpRequest();
	            queued.request.onreadystatechange = this.progress;
	            queued.request.open(queued.method, queued.url, true);
	            if (queued.method == 'POST') {
	                queued.request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	                queued.request.setRequestHeader("Content-length", queued.post.length);
	                queued.request.setRequestHeader("Connection", "close");
	            }
	            queued.request.send(queued.post);
	            // branch for IE/Windows ActiveX version
	        } else if (window.ActiveXObject) {
	            queued.request = new ActiveXObject("Microsoft.XMLHTTP");
	            queued.request.onreadystatechange = this.progress;
	            queued.request.open(queued.method, queued.url, true);
	            if (queued.method == 'POST') {
	                queued.request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	                queued.request.setRequestHeader("Content-length", queued.post.length);
	                queued.request.setRequestHeader("Connection", "close");
	            }
	            queued.request.send(queued.post);
	            // if all else fails: load the document in an iFrame
	        } else if (window.frames) {
	            // create an iframe to read the document in
	            objIframe = document.createElement("IFRAME");
	            objIframe.src = queued.url;
	            objIframe.id = "feedimport0";
	            objIframe.name = "feedimport0";
	            objIframe.style = "visibility : invisible; position : absolute; left : -1600px; top : -1600px;";
	            //objIframe.onload = ajax_load; // Doesn't work in Opera
	            // append the iframe to the document
	            document.body.appendChild(objIframe);
	            // wait for the iframe to load
	            this.wait();
	        }
	    },
	    handleQueue: function() {
	        queue = jQuery.classBehaviours.ajax.queue;
	        // if the first item in the queue is a completed request
	        if (queue.length > 0) {
	            if (queue[0].ready == 4 /*&& ajax.queue[0].status==200*/) {
	                // remove the completed request
	                queue.reverse();
	                queue.length = queue.length - 1;
	                queue.reverse();
	            }
	        }
	        // if the first item in the queue isn't allready in progress
	        if (queue.length > 0) {
	            if (!(queue[0].ready < 4 && queue[0].ready != null)) {
	                this.makeRequest(queue[0]);
	            }
	        }
	    },
	    // events
	    progress: function() {
	        queued = jQuery.classBehaviours.ajax.queue[0];
	        // remember the readyState
	        queued.ready = queued.request.readyState;
	        // only if req shows "complete"
	        if (queued.request.readyState == 4) {
	            // remember the status
	            queued.status = queued.request.status;
	            // only if "OK"
	            if (queued.request.status == 200 || queued.request.status == 304) {
	                // update optional progress indicator code
	                if (queued.doOnProgress) queued.doOnProgress(1, queued.referObject, queued.request.status);
	                // get the imported text
	                queued.text = queued.request.responseText;
	                // get the imported document
	                queued.document = queued.request.responseXML;
	                // if the document is empty use a deserialized version of the text instead
	                if (queued.document == null) queued.document = jQuery.classBehaviours.ajax.deserialize(queued.text)
	                else if (queued.document.childNodes.length == 0) queued.document = jQuery.classBehaviours.ajax.deserialize(queued.text);
	                // trigger the load event
	                if (queued.doOnLoad) queued.doOnLoad(queued.document, queued.referObject, queued.text);
	                // request the next item in the queue to be handled
	                jQuery.classBehaviours.ajax.handleQueue();
	            } else {
	                // update optional progress indicator code
	                if (queued.doOnProgress) queued.doOnProgress(-1, queued.referObject, queued.request.status);
	            }
	        } else {
	            // update optional progress indicator code
	            if (queued.doOnProgress) queued.doOnProgress(queued.request.readyState, queued.referObject, 200);
	        }
	        // return the status if desired
	        return queued.request.readyState;
	    },
	    wait: function() {
	        queued = jQuery.classBehaviours.ajax.queue[0];
	        // if the xml document has loaded in the iframe
	        if (window.frames["feedimport0"]) {
	            // define the xml document object
	            queued.document = window.frames["feedimport0"].document;
	            queued.text = window.frames["feedimport0"].document.body.innerHTML;
	            // what to do after the xml document loads
	            queued.doOnLoad(queued.document, queued.referObject, queued.text);
	            // else try again in a while
	        } else {
	            setTimeout("ajax.wait()", 256);
	        }
	    }
	}
		// prototype http request object
		jQuery.classBehaviours.ajax.HttpRequest = function(){
			// request constants
			this.idx			=	null;
			this.url			=	null;
			this.post			=	null;
			this.method			=	'GET';
			// request events
			this.doOnLoad		=	null;
			this.doOnProgress	=	null;
			this.referObject	=	null;
			// request properties
			this.request		=	null;
			this.document		=	null;
			this.text			=	null;
			this.ready			=	null;
			this.status			=	null;
		}

	// debug console
	jQuery.classBehaviours.console = {
		// porperties
		id: 'debugConsole0',
		limit: 32,
		// methods
		debug: function(){
			// create the debug console if it doesn't exist yet
			debugConsole = document.getElementById(jQuery.classBehaviours.console.id);
			if(debugConsole==null){
				// create a new console node
				newConsole = document.createElement('div');
				newConsole.id = jQuery.classBehaviours.console.id;
				newText = document.createTextNode('-- newest at top --');
				newConsole.appendChild(newText);
				document.body.appendChild(newConsole);
				debugConsole = document.getElementById(jQuery.classBehaviours.console.id);
			}
			// set the console's properties
			if(debugConsole.style.background=='') 	debugConsole.style.background = '#ffffff url(../images/button_passive.png) no-repeat 0px 0px';
			if(debugConsole.style.border=='') 		debugConsole.style.border = 'solid 1px #000000';
			if(debugConsole.style.bottom=='') 		debugConsole.style.bottom = 'auto';
			if(debugConsole.style.height=='') 		debugConsole.style.height = (navigator.userAgent.indexOf('MSIE 6')>-1) ? '580px' : '98%' ;
			if(debugConsole.style.left=='') 		debugConsole.style.left = 'auto';
			if(debugConsole.style.overflow=='') 	debugConsole.style.overflow = 'auto';
			if(debugConsole.style.padding=='') 		debugConsole.style.padding = '1% 1% 1% 1%';
			if(debugConsole.style.position=='') 	debugConsole.style.position = (navigator.userAgent.indexOf('MSIE 6')>-1) ? 'absolute' : 'fixed' ;
			if(debugConsole.style.right=='') 		debugConsole.style.right = '-1px';
			if(debugConsole.style.top=='') 			debugConsole.style.top = '-1px';
			if(debugConsole.style.width=='') 		debugConsole.style.width = '128px';
			debugConsole.onclick = jQuery.classBehaviours.console.move;
			jQuery.classBehaviours.fader.setFade(debugConsole, 75);
			// send all the incoming arguments to the debug console
			newList = document.createElement('ul');
			for(var a=0; a<arguments.length; a++){
				// create a list item for each argument
				newListItem = document.createElement('li');
				newListText = document.createTextNode(arguments[a]);
				newListItem.appendChild(newListText);
				newList.appendChild(newListItem);
			}
			debugConsole.insertBefore(newList, debugConsole.firstChild);
			// if there are too many enties, remove a few
			allLists = debugConsole.getElementsByTagName('ul');
			if(allLists.length>jQuery.classBehaviours.console.limit)
				for(var a=allLists.length-1; a>jQuery.classBehaviours.console.limit; a--)
					removedChild = debugConsole.removeChild(allLists[allLists.length-1]);
		},
		clear: function(){
			// clear the entries
			debugConsole = document.getElementById(jQuery.classBehaviours.console.id);
			if(debugConsole!=null){
				allLists = debugConsole.getElementsByTagName('ul');
				for(var a=allLists.length-1; a>=0; a--)
					removedChild = debugConsole.removeChild(allLists[allLists.length-1]);
			}
		},
		html: function(string){
			string = '<xmp>' + string + '</xmp>'
			this.debug(string)
		},
		// events
		move: function(that){
			var node = (typeof(this.nodeName)=='undefined') ? that : this ;
			// get the console
			debugConsole = document.getElementById(jQuery.classBehaviours.console.id);
			// toggle through several console positions
			if(debugConsole.style.right == '-1px' && debugConsole.style.width == '128px'){
				debugConsole.style.bottom = 'auto';
				debugConsole.style.height = (navigator.userAgent.indexOf('MSIE 6')>-1) ? '580px' : '98%' ;
				debugConsole.style.left = '-1px';
				debugConsole.style.right = 'auto';
				debugConsole.style.top = '-1px';
				debugConsole.style.width = '128px';
				debugConsole.style.zIndex = '100000';
			}else if(debugConsole.style.left == '-1px' && debugConsole.style.width == '128px'){
				debugConsole.style.bottom = '-1px';
				debugConsole.style.height = '128px';
				debugConsole.style.left = '-1px';
				debugConsole.style.right = 'auto';
				debugConsole.style.top = 'auto';
				debugConsole.style.width = '98%';
				debugConsole.style.zIndex = '100000';
			}else if(debugConsole.style.bottom == '-1px' && debugConsole.style.width == '98%'){
				debugConsole.style.bottom = 'auto';
				debugConsole.style.height = (navigator.userAgent.indexOf('MSIE 6')>-1) ? '580px' : '98%' ;
				debugConsole.style.left = 'auto';
				debugConsole.style.right = '-1px';
				debugConsole.style.top = '-1px';
				debugConsole.style.width = '128px';
				debugConsole.style.zIndex = '100000';
			}
		}
	}

	// cookies Library
	jQuery.classBehaviours.cookies = {
		// methods
		getDaysFromNow: function(intDays){
			var dateCurrent = new Date();
			var intCurrent = Date.parse(dateCurrent);
			var intPeriod = intDays*24*60*60*1000;
			var datePeriodInFuture = new Date(intCurrent+intPeriod);
			return datePeriodInFuture;
		},
		setCookie: function(name, value, expires, path, domain, secure) {
			var curCookie = name + "=" + escape(value) +
				((expires) ? "; expires=" + expires.toGMTString() : "") +
				((path) ? "; path=" + path : "") +
				((domain) ? "; domain=" + domain : "") +
				((secure) ? "; secure" : "");
			document.cookie = curCookie;
		},
		getCookie: function(name) {
			var dc = document.cookie;
			var prefix = name + "=";
			var begin = dc.indexOf("; " + prefix);
			if (begin == -1) {
				begin = dc.indexOf(prefix);
				if (begin != 0) return null;
			}else{
				begin += 2;
			}
			var end = document.cookie.indexOf(";", begin);
			if (end == -1)
				end = dc.length;
			return unescape(dc.substring(begin + prefix.length, end));
		},
		deleteCookie: function(name, path, domain) {
			if (getCookie(name)) {
				document.cookie = name + "=" +
				((path) ? "; path=" + path : "") +
				((domain) ? "; domain=" + domain : "") +
				"; expires=Thu, 01-Jan-70 00:00:01 GMT";
			}
		},
		fixDate: function(date) {
			var base = new Date(0);
			var skew = base.getTime();
			if (skew > 0)
				date.setTime(date.getTime() - skew);
		},
		makeBoolean: function(strIn){
			if(strIn=='true'){
				booOut = true;
			}else{
				booOut = false;
			}
			return booOut;
		},
		csv2array: function(csvIn){
			return csvIn.split(',');
		},
		array2csv: function(arrIn){
			return ''+arrIn;
		}
	}

	// decorative fader animations
	jQuery.classBehaviours.fader = {
		// properties
		getFade: function(node){
			var fadeValue = null;
			if(node!=null){
				// get the fade value using the proper method
				if(typeof(node.style.MozOpacity)!='undefined')	fadeValue = Math.round(parseFloat(node.style.MozOpacity)*100);
				if(typeof(node.style.filter)!='undefined')		fadeValue = parseInt(node.filters.alpha.opacity);
				if(typeof(node.style.opacity)!='undefined')		fadeValue = Math.round(parseFloat(node.style.opacity)*100);
			}
			// return the value
			return fadeValue;
		},
		setFade: function(node, amount){
			if(node!=null){
				// set the fade value using the proper method
				if(typeof(node.style.MozOpacity)!='undefined')	node.style.MozOpacity = amount/100;
				if(typeof(node.style.filter)!='undefined')		node.style.filter = "alpha(opacity=" + amount + ")";
				if(typeof(node.style.opacity)!='undefined')		node.style.opacity = amount/100;
			}
			/*
			filter:alpha(opacity=50);	imageobject.filters.alpha.opacity=opacity
			-moz-opacity: 0.5;			imageobject.style.MozOpacity=opacity/100
			opacity: 0.5;
			-khtml-opacity: 0.5;
			*/
		},
		getSize: function(node){
			// measure the height of the container
			var nodeWidth = node.offsetWidth;
			var nodeHeight = node.offsetHeight;
			// measure the height of all the childnodes of the container
			var totalWidth = 0;
			var totalHeight = 0;
			var contents = node.childNodes;
			for(var a=0; a<contents.length; a++){
				totalWidth += (contents[a].offsetWidth) ? contents[a].offsetWidth : 0 ;
				totalHeight += (contents[a].offsetHeight) ? contents[a].offsetHeight : 0 ;
			}
			// pass back the largest number
			return new Array(nodeWidth, nodeHeight, totalWidth, totalHeight);
		},
		setSize: function(node, xAmount, yAmount){
			if(xAmount!=null) node.style.width = xAmount + 'px';
			if(yAmount!=null) node.style.height = yAmount + 'px';
		},
		// methods
		fade: function(id, start, end, step, delay, acceleration, evalOnEnd){
			var cf = jQuery.classBehaviours.fader;
			// get the target node
			target = document.getElementById(id);
			// get the start value if missing
			if(start==null) 		start = cf.getFade(target);
			if(end==null) 			end = 100;
			if(step==null) 			step = 1;
			if(delay==null) 		delay = 10;
			if(acceleration==null) 	acceleration = 1;
			if(evalOnEnd==null) 	evalOnEnd = '';
			// calculate the new value
			if(start<end)		{value = (start+step>end) ? end : start+step ;}
			else if(start>end)	{value = (start-step<end) ? end : start-step ;}
			// set the fade
			cf.setFade(target, value);
			// order the next step
			if(value!=end) 		{setTimeout("jQuery.classBehaviours.fader.fade('"+id+"',"+value+","+end+","+(step+acceleration)+","+delay+","+acceleration+",'"+evalOnEnd+"')", delay);}
			else 				{eval(evalOnEnd);}
		},
		size: function(id, start, end, step, delay, acceleration, evalOnEnd){
			var cf = jQuery.classBehaviours.fader;
			// get the target node
			target = document.getElementById(id);
			// get the start value if missing
			if(start==null) 		start = cf.getSize(target)[1];
			if(end==null) 			end = cf.getSize(target)[3];
			if(step==null) 			step = 10;
			if(delay==null) 		delay = 10;
			if(acceleration==null) 	acceleration = 10;
			if(evalOnEnd==null) 	evalOnEnd = '';
			// calculate the new value
			if(start<end)		{value = (start+step>end) ? end : start+step ;}
			else if(start>end)	{value = (start-step<end) ? end : start-step ;}
			// set the fade
			cf.setSize(target, null, value);
			// order the next step
			if(value!=end) 		{setTimeout("jQuery.classBehaviours.fader.size('"+id+"',"+value+","+end+","+(step+acceleration)+","+delay+","+acceleration+",'"+evalOnEnd+"')", delay);}
			else 				{eval(evalOnEnd);}
		},
		/* START: legacy functions */
		fadeIn: function(id, step, delay, evalOnEnd, acceleration){
			jQuery.classBehaviours.fader.fade(id, 0, 100, step, delay, acceleration, evalOnEnd);
		},
		fadeOut: function(id, step, delay, evalOnEnd, acceleration){
			jQuery.classBehaviours.fader.fade(id, 100, 0, step, delay, acceleration, evalOnEnd);
		},
		crossFade: function(idIn, idOut, amount, step, delay, evalOnEnd, acceleration){
			jQuery.classBehaviours.fader.fade(idIn, 0, 100, step, delay, acceleration, evalOnEnd);
			jQuery.classBehaviours.fader.fade(idOut, 100, 0, step, delay, acceleration, '');
		},
		grow: function(id, step, delay, evalOnEnd, acceleration){
			jQuery.classBehaviours.fader.size(id, 1, null, step, delay, acceleration, evalOnEnd);
		},
		shrink: function(id, step, delay, evalOnEnd, acceleration){
			jQuery.classBehaviours.fader.size(id, null, 1, step, delay, acceleration, evalOnEnd);
		}
		/* END: legacy functions */
	}

	// general purpose functions
	jQuery.classBehaviours.utilities = {
		// returns all nodes of the same class
		getElementsByClassName: function(className, node){
			// use the whole body if no target was provided
			target = (node!=null) ? node : document ;
			// make an empty array for the results
			var foundNodes = new Array();
			// for all elements in the parent node
			var allNodes = (target.all) ? target.all : target.getElementsByTagName("*");
			for(var a=0; a<allNodes.length; a++){
				// if the item has a className
				if(allNodes[a].className){
					// process the classname
					nodeClass = allNodes[a].className + ' ';
					// add it to the results if the classname was found
					if(nodeClass.indexOf(className+' ')>-1) foundNodes[foundNodes.length] = allNodes[a];
				}
			}
			// return the list
			return foundNodes;
		},
		// emulates getElementById for XML documents
		getXmlElementById: function(id, doc, echo){
			// default value
			node = null ;
			// if a valid id was given
			if(id!=null){
				// look through all nodes
				allNodes = doc.getElementsByTagName('*');
				// until you find one with the right ID
				var a = 0;
				while(node==null && a<allNodes.length){
					if(allNodes[a].getAttribute('id')==id){
						node = allNodes[a];
					}
					a += 1;
				}
			}
			// return the cleaned content
			return (echo && node==null) ? doc : node;
		},
		// places a sibling node after a given node
		insertAfter: function(parentNode, siblingNode, newNode){
			nextNode = jQuery.classBehaviours.utilities.nextNode(siblingNode);
			if(nextNode==siblingNode) parentNode.appendChild(newNode)
			else parentNode.insertBefore(newNode, nextNode);
		},
		// detaries the available room on the screen
		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
		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 ;
		},
		// gets the value of a parameter from a className
		getClassParameter: function(targetNode, paramName, defaultValue){
			if(targetNode!=null){
				// get the class parameter from the classname
				var classParameter = targetNode.className;
				// split the classname between the parameter name
				classParameter = (classParameter!=null) ? classParameter.split(paramName + '_') : new Array();
				// 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 ;
			}
		},
		// sets the value of a parameter from a className
		setClassParameter: function(targetNode, paramName, newValue){
			if(targetNode!=null){
				// create a default value, if there isn't one
				if(targetNode.className.indexOf(' ' + paramName + '_')<0) targetNode.className += ' ' + paramName + '_0';
				// get the old value
				oldValue = this.getClassParameter(targetNode, paramName, null);
				// replace the old value
				if(oldValue!=null) targetNode.className = targetNode.className.replace(paramName+'_'+oldValue, paramName+'_'+newValue);
			}
		},
		// get the next node without worrying about text nodes
		nextNode: function(node, count){
			testNode = node;
			if(count==null) count = 1;
			// look for the next html node
			for(var a=0; a<count; a++){
				do {
					testNode = testNode.nextSibling;
					if(testNode==null) testNode = node;
				}while(testNode.nodeName.indexOf('#text')>-1);
			}
			// return it
			return testNode;
		},
		// get the previous node without worrying about text nodes
		previousNode: function(node, count){
			testNode = node;
			if(count==null) count = 1;
			// look for the previous html node
			for(var a=0; a<count; a++){
				do {
					testNode = testNode.previousSibling;
					if(testNode==null) testNode = node;
				}while(testNode.nodeName.indexOf('#text')>-1);
			}
			// return it
			return testNode;
		},
		// get the first real child node without worrying about text nodes
		firstNode: function(node, count){
			return this.nextNode(node.firstChild, count);
		},
		// find the parent node with the given classname
		rootNode: function(node, rootTag, rootId, rootClass){
			// try parent nodes until you find the one which meets the conditions
			rootFound = false;
			while(!rootFound && node.nodeName!='BODY'){
				rootFound = (rootTag && node.nodeName) ? (node.nodeName.indexOf(rootTag)>-1) : rootFound ;
				rootFound = (rootId && node.id) ? (node.id.indexOf(rootId)>-1) : rootFound ;
				rootFound = (rootClass && node.className) ? (node.className.indexOf(rootClass)>-1) : rootFound ;
				node = (!rootFound) ? node.parentNode : node;
			}
			// pass it back
			return node;
		},
		// returns the visible display state needed for this element
		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;
		},
		// hide select form elements for graphic glitches in certain browsers
		hideSelects: function(node){
			if(
				navigator.userAgent.indexOf('MSIE 6')>-1 ||
				(navigator.userAgent.indexOf('Firefox/2')>-1 && navigator.userAgent.indexOf('Mac')>-1)
			){
				allSelects = (node) ? node.getElementsByTagName('SELECT') : document.getElementsByTagName('SELECT') ;
				for(var a=0; a<allSelects.length; a++){
					allSelects[a].style.visibility = 'hidden';
				}
			}
		},
		showSelects: function(node){
			if(
				navigator.userAgent.indexOf('MSIE 6')>-1 ||
				(navigator.userAgent.indexOf('Firefox/2')>-1 && navigator.userAgent.indexOf('Mac')>-1)
			){
				allSelects = (node) ? node.getElementsByTagName('SELECT') : document.getElementsByTagName('SELECT') ;
				for(var a=0; a<allSelects.length; a++){
					allSelects[a].style.visibility = 'visible';
				}
			}
		},
		// adds an event-handler the proper way
		addEvent: function(node, eventName, eventHandler){
			if(node.addEventListener) 	node.addEventListener(eventName, eventHandler, false)
			else if(node.attachEvent) 	node.attachEvent("on"+eventName, eventHandler)
			else 						node["on"+eventName] = eventHandler;
			return true;
		}
	}

	// Parser functions
	jQuery.classBehaviours.parser = {
	    // status of the parser
	    waiting: false,
	    // verify the state of the object
	    start: function() {
	        // if the document is complete enough start the behaviours, else wait until everything is loaded
	        var handlersCount = 0;
	        for (b in jQuery.classBehaviours.handlers) handlersCount++;
	        this.waiting = (handlersCount > 0 && document.body) ? this.parseDocument() : jQuery.classBehaviours.utilities.addEvent(window, 'load', this.parseDocument);
	    },
	    // scan the whole document
	    parseDocument: function() {
	        // pass the document object to the parser
	        jQuery.classBehaviours.parser.parseNode(document);
	        // return the status
	        return false;
	    },
	    // scan the document object model for target classNames
	    parseNode: function(node) {
	        // for all childnodes of the given node
	        var allNodes = node.getElementsByTagName("*");
	        for (var a = 0; a < allNodes.length; a++) {
	            // process the classnames of the node
	            this.processNode(allNodes[a]);
	        }
	        // do the same for the parent node
	        this.processNode(node);
	    },
	    // recursive version of the same function
	    parseNode: function(node) {
	        // process the node
	        parseChildNodes = (node.className != null) ? this.processNode(node) : true;
	        // parse any childnodes
	        if (parseChildNodes) for (var a = 0; a < node.childNodes.length; a++) this.parseNode(node.childNodes[a]);
	    },
	    // process the classnames of the node
	    processNode: function(node) {
	        // get its className
	        nodeClass = ' ' + node.className + ' ';
	        // for all class behaviours
	        for (b in jQuery.classBehaviours.handlers) {
	            // if the behaviour name is in the className tested node
	            if (nodeClass.indexOf(' ' + jQuery.classBehaviours.handlers[b].name + ' ') > -1) {
	                // apply its respective behaviour
	                if (jQuery.classBehaviours.handlers[b].start != null)
	                    jQuery.classBehaviours.handlers[b].start(node);
	            }
	        }
	        // decide if to continue parsing deeper into this branch
	        return (nodeClass.indexOf('doNotParse') < 0);
	    }
	}

	// short-cut wrapper functions
	debug = jQuery.classBehaviours.console.debug;
	getXmlElementById = jQuery.classBehaviours.utilities.getXmlElementById;
	getElementsByClassName = jQuery.classBehaviours.utilities.getElementsByClassName;

	// add this addon to the jQuery object
	if(typeof(jQuery.fn)!='undefined'){
		// set the event handler for this jQuery method
		$(document).ready(
			function(){
// TODO: at the moment this is handled by the individual classbehaviours modules, until this proves too slow
//				$.classBehaviours.parser.parseDocument()
			}
		);
	}else{
		// start the (backup) parsing of classes if the main jQuery library is not available
		jQuery.classBehaviours.parser.start()
	}

