
/*
	Source: classbehaviours.handlers.reloadfromurl.js
	ClassBehaviours is a javascript framework based on class-name parsing.
	Copyright 2011 by Maurice van Creij and published on http://www.woollymittens.nl/
	This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
*/

	// CLASSBEHAVIOURS CLASS
	// create the root classbehaviours object if it doesn't already exist
	if(typeof(classBehaviours)=='undefined') classBehaviours = {};

		// CLASSNAME BEHAVIOUR FUNCTIONS
		// create the handlers child object if it doesn't already exist
		if(typeof(classBehaviours.handlers)=='undefined') classBehaviours.handlers = {}

			// Replace a link to external content with the actual content
			classBehaviours.handlers.reloadFromUrl = {
				// properties
				name: 'reloadFromUrl',
				settings: new Array(),
				refocus: '',
				index: 0,
				respite: 50,
				// methods
				start: function(node){
					// give the node an id, if it doesn't have one yet
					node.id = (node.id) ? node.id : this.name + this.index++ ;
					this.settings[node.id] = {};
					// shortcut pointers
					var rfu = classBehaviours.handlers.reloadFromUrl;
					var cfg = rfu.settings[node.id];
					// if the node is marked to load automatically start loading
					cfg.autoTimeout = null;
					automatic = classBehaviours.utilities.getClassParameter(node, 'auto', 'no');
					if(automatic=='yes'){
						this.clicked(node);
					}else if(parseInt(automatic)>0){
						clearTimeout(cfg.autoTimeout);
						cfg.autoTimeout = setTimeout('classBehaviours.handlers.reloadFromUrl.clicked(document.getElementById("'+node.id+'"))', parseInt(automatic));
					}
					// set an onclick event
					if(node.nodeName == 'IFRAME') this.clicked(node)
					else if(node.nodeName == 'FORM') node.onsubmit = this.clicked
					else if(node.nodeName == 'BUTTON') node.onclick = this.clicked
					else if(node.nodeName == 'INPUT' && (node.type=='button' || node.type=='submit')) node.onclick = this.clicked
					else if(node.nodeName == 'INPUT' && (node.type=='radio' || node.type=='checkbox')) node.onclick = this.changed
					else if(node.nodeName == 'INPUT') node.onchange = this.changed
					else if(node.nodeName == 'SELECT') node.onchange = this.changed
					else if(node.nodeName == 'TEXTAREA') node.onchange = this.changed
					else if(node.nodeName == 'FIELDSET') null
					else node.onclick = this.clicked;
					//  Added for hijacking .NET postback event
					if(node.nodeName=='A' && node.href.indexOf('javascript:__doPostBack')>-1){
						node.href = '';
						node.onclick = this.clicked;
					}
				},
				wait: function(importProgress, referedNode, importError, requestTime){
					var rfu = classBehaviours.handlers.reloadFromUrl;
					// get the target for the indicator
					targetId = classBehaviours.utilities.getClassParameter(referedNode, 'target', '');
					if(targetId!='') targetNode = document.getElementById(targetId)
					else targetNode = referedNode;
					// if a progress meter was explicitly defined
					progressNode = classBehaviours.utilities.getClassParameter(referedNode, 'progress', '');
					if(progressNode!='') targetNode = document.getElementById(progressNode);
					// update the progress indicator
					classBehaviours.utilities.setClassParameter(targetNode, 'progress', importProgress);
					classBehaviours.utilities.setClassParameter(targetNode, 'error', importError);
					if(targetNode.nodeName=='METER'){
						targetNode.setAttribute('min', 0);
						targetNode.setAttribute('max', 100);
						targetNode.setAttribute('value', importProgress/4);
					}
					// only turn on the progress indicator after a short pause
					shownStatus = (new Date()-requestTime > rfu.respite || requestTime<0) ? 1 : 0 ;
					classBehaviours.utilities.setClassParameter(targetNode, 'show', shownStatus);
				},
				insert: function(importedObj, referedNode, importedText, requestTime){
					var rfu = classBehaviours.handlers.reloadFromUrl;
					// get the optional parameters from the refered node
					targetId = classBehaviours.utilities.getClassParameter(referedNode, 'target', null);
					sourceId = classBehaviours.utilities.getClassParameter(referedNode, 'source', null);
					//  if a form is in the response take the action and reload
					noformId = classBehaviours.utilities.getClassParameter(referedNode, 'noform', null);
					nonIntrusive = (classBehaviours.utilities.getClassParameter(referedNode, 'gently', '0')=='1');
					// for all peers of the clicked node
					if(referedNode.parentNode!=null){
						var allPeers = referedNode.parentNode.parentNode.getElementsByTagName(referedNode.nodeName);
						for(var a=0; a<allPeers.length; a++){
							// mark the clicked node as active
							if(allPeers[a]==referedNode){
								allPeers[a].className = allPeers[a].className.replace('link', 'active');
							}
							// mark the peers of the clicked node as passive
							else{
								allPeers[a].className = allPeers[a].className.replace('active', 'link');
							}
						}
					}
					// get the content from the imported node
					importedNode = (sourceId!=null) ?
						rfu.getXmlElementById(sourceId, importedObj, true) :
						importedObj ;
					// forcefully insert the content into the target node
					if(!nonIntrusive){
						// get the source HTML from the imported document
						importedHTML = rfu.serialize(importedNode, true);
						// strip any left over body tags from the content
						if(importedHTML.indexOf('<body')>-1){
							importedHTML = importedHTML.split('<body')[1].split('</body>')[0];
							importedHTML = importedHTML.substr(importedHTML.indexOf('>') + 1, importedHTML.length);
						}
						// if no id was given for the target node
						if(targetId==null){
							// replace the refered node
							newDiv = document.createElement('div');
							newDiv.id = rfu.name + rfu.index++ ;
							referedNode.parentNode.replaceChild(newDiv, referedNode);
							var targetNode = document.getElementById(newDiv.id);
						}
						// else
						else{
							// use the target id to find the node
							var targetNode = document.getElementById(targetId);
						}
						//  if a full page is loaded and the noform is set do a redirect.
						if (noformId == '1' && new String(importedHTML).toLowerCase().indexOf('<form') > -1) {
							url = new String(importedHTML).toLowerCase();
							index = url.indexOf('action') + 8;
							url = url.substr(index, url.length - index);
							//  IE removed the " from the action="".
							index = url.indexOf('"') > url.indexOf('>') ? url.indexOf('>') : url.indexOf('"');
							url = url.substr(0, index);
							location.href = url;
							return;
						}
						// insert the imported HTML
						if(targetNode!=null){
							// if the new content needs to be revealed gradually
							var revealTime = parseInt(classBehaviours.utilities.getClassParameter(referedNode, 'reveal', '0'));
							if(revealTime>0){
								// hide the old content and reveal the new content
								classBehaviours.fader.fade(targetNode, 100, 0, 5000/revealTime, 50, 0, function(){
									targetNode.innerHTML = importedHTML;
									classBehaviours.fader.fade(targetNode, 0, 100, 5000/revealTime, 50, 0, null);
								});
							}
							// else just show it immediately
							else{
								targetNode.innerHTML = importedHTML;
							}
						}
						// set the focus (if any)
						if(rfu.refocus!=null){
							if(rfu.refocus.nodeName=='INPUT' || rfu.refocus.nodeName=='SELECT' || rfu.refocus.nodeName=='TEXTAREA'){
								rfu.refocus.focus();
							}
						}
					}
					// or gently compare classnames and text
					else{
						// for all imported nodes
						importedChildNodes = importedNode.getElementsByTagName('*');
						for(var a=importedChildNodes.length-1; a>=0; a--){
							// get the target ID
							exportedChildNodeId = importedChildNodes[a].getAttribute('id');
							// if it's a node with an existing ID
							if(exportedChildNodeId!=null && exportedChildNodeId!=''){
								// get the target node
								exportedChildNode = document.getElementById(exportedChildNodeId);
								// does this node actually exist?
								if(exportedChildNode!=null){
									// sync the classname
									exportedChildNode.className = (importedChildNodes[a].className!=null) ? importedChildNodes[a].className : importedChildNodes[a].getAttribute('class') ;
									// sync the select options
									if(importedChildNodes[a].nodeName=='SELECT'){
										// every node in this select
										importedSelectOptions = importedChildNodes[a].getElementsByTagName('OPTION');
										exportedSelectOptions = exportedChildNode.getElementsByTagName('OPTION');
										// choose the longest list
										selectOptionsLength = (importedSelectOptions.length>exportedSelectOptions.length) ? importedSelectOptions.length : exportedSelectOptions.length ;
										// for every item in the list
										for(var b=selectOptionsLength-1; b>=0; b--){
											// if there is no exported option
											if(b>=exportedSelectOptions.length){
												// make a new option node
												newOptionElement = document.createElement('OPTION');
												newOptionText = document.createTextNode(importedSelectOptions[b].firstChild.nodeValue);
												newOptionElement.appendChild(newOptionText);
												newOptionElement.setAttribute('value', importedSelectOptions[b].value);
												exportedChildNode.appendChild(newOptionElement);
											}
											// if there is no imported option
											else if(b>=importedSelectOptions.length){
												// remove an option node
												exportedSelectOptions[b].parentNode.removeChild(exportedSelectOptions[b]);
											}
											// else
											else{
												// transfer the imported to the exported node
												exportedSelectOptions[b].firstChild.nodeValue = importedSelectOptions[b].firstChild.nodeValue;
												exportedSelectOptions[b].value = importedSelectOptions[b].value;
											}
										}
									}
									// sync the text
									importedHTML = rfu.serialize(importedChildNodes[a]);
									importedHTML = importedHTML.substring(importedHTML.indexOf('>') + 1, importedHTML.lastIndexOf('<'));
									isInlineMarkup = (
										exportedChildNode.innerHTML.toUpperCase().indexOf('<DIV')<0 &&
										exportedChildNode.innerHTML.toUpperCase().indexOf('<SECTION')<0 &&
										exportedChildNode.innerHTML.toUpperCase().indexOf('<ARTICLE')<0 &&
										exportedChildNode.innerHTML.toUpperCase().indexOf('<HEADER')<0 &&
										exportedChildNode.innerHTML.toUpperCase().indexOf('<FOOTER')<0 &&
										exportedChildNode.innerHTML.toUpperCase().indexOf('<NAV')<0 &&
										exportedChildNode.innerHTML.toUpperCase().indexOf('<FIELDSET')<0 &&
										exportedChildNode.innerHTML.toUpperCase().indexOf('<SELECT')<0 &&
										exportedChildNode.nodeName!='SELECT' &&
										exportedChildNode.innerHTML.toUpperCase().indexOf('<OPTION')<0 &&
										exportedChildNode.innerHTML.toUpperCase().indexOf('<TEXTAREA')<0 &&
										exportedChildNode.nodeName!='TEXTAREA' &&
										exportedChildNode.innerHTML.toUpperCase().indexOf('<INPUT')<0 &&
										exportedChildNode.nodeName!='INPUT'
									);
									if(isInlineMarkup) exportedChildNode.innerHTML = importedHTML;
								}
							}
						}
					}
					// reset the progress indicator
					classBehaviours.utilities.setClassParameter(targetNode, 'progress', 4);
					classBehaviours.utilities.setClassParameter(targetNode, 'error', 200);
					// activate any classbehaviours in there
					classBehaviours.parser.parseNode(targetNode);
					// OPTIONAL: replace the title
					replaceTitleId = classBehaviours.utilities.getClassParameter(referedNode, 'title', null);
					if(replaceTitleId!=null) document.getElementById(replaceTitleId).innerHTML = referedNode.title;
				},
				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;
				},
				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;
				},
				getPostValues : function(node, nodeId){
					// use the id if needed
					if(nodeId!=null) node = document.getElementById(nodeId);
					// get all elements of this form
					formNode = classBehaviours.utilities.findParentNode(node, 'FORM');
					rootNode = classBehaviours.utilities.findParentNode(node, 'FIELDSET');
					allNodes = rootNode.getElementsByTagName('*');
					postValues = '';
					// see if we can do anything to make it easier for .NET
					// .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;
					postValues = '__EVENTTARGET=' + eventTarget + '&';
					// 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 except the viewstate and eventtarget (.NET)
								if(allNodes[a].name!='__VIEWSTATE' && allNodes[a].name!='__EVENTTARGET')
									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.id + '=' + node.value + '&';
					// add the cookie value too
					if (document.cookie != null) postValues += document.cookie.replace(/; /gi, "&");
					// return the values
					return postValues;
				},
				// events
				clicked: function(that, href){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var rfu = classBehaviours.handlers.reloadFromUrl;
					// if the node still exists
					if(objNode!=null){
						// reminder the focus
						rfu.refocus = objNode;
						// try to get a URL from somewhere
						targetHref = (href==null) ? objNode.getAttribute('href') : href ;
						targetSrc = objNode.getAttribute('src');
						targetLongdesc = objNode.getAttribute('longdesc');
						rootForm = classBehaviours.utilities.findParentNode(objNode, 'FORM', null);
						targetAction = rootForm.getAttribute('action');
						//  get the id reference with holds the URI information
						hrefTarget = classBehaviours.utilities.getClassParameter(objNode, 'id', '');
						// decide which URL has priority
						targetUrl = (targetHref != null && targetHref != '') ? targetHref :
							(hrefTarget != null && hrefTarget != '') ? document.getElementById(hrefTarget).href :
							(targetAction != null && targetAction != '') ? targetAction :
							(targetLongdesc != null && targetLongdesc != '') ? targetLongdesc :
							(targetSrc != null && targetSrc != '') ? targetSrc :
							document.location.href;
						// get the post values
						postValues = rfu.getPostValues(objNode);
						// get the post method
						postPrefix = (targetUrl.indexOf('?')<0) ? '?' : '&' ;
						// if this is to be a refresh loop, set the interval for it
						reloadInterval = parseInt(classBehaviours.utilities.getClassParameter(objNode, 'interval', '-1'));
						if(reloadInterval>0){
							setTimeout('classBehaviours.handlers.reloadFromUrl.clicked(document.getElementById("'+objNode.id+'"));', reloadInterval);
						}
						// place the AJAX request and cancel the click
						return (new String(rootForm.getAttribute('method')).toLowerCase() == 'post') ?
							classBehaviours.ajax.addRequest(targetUrl, rfu.insert, rfu.wait, postValues, objNode) :
							classBehaviours.ajax.addRequest(targetUrl + postPrefix  + postValues, rfu.insert, rfu.wait, null, objNode) ;
					}
				},
				changed: function(that){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var rfu = classBehaviours.handlers.reloadFromUrl;
					// if the form element is a select filled with url's, assume this is used as navigation and not as form input
					if(objNode.value.indexOf('/')>-1 && objNode.value.indexOf('.')>-1){
						// take the url and use it as an override in the ajax call
						rfu.clicked(objNode, objNode.value);
						return false;
					}else{
						// find the fieldset that belongs to this part of the form
						submitNode = classBehaviours.utilities.findParentNode(objNode, 'FIELDSET', null);
						// use the entire form if no fieldset was found
						if(submitNode.nodeName=='BODY'){
							submitNode = classBehaviours.utilities.findParentNode(objNode, 'FORM', null);
						}
						// find any buttons in this fieldset
						buttonNodes = submitNode.getElementsByTagName('BUTTON');
						if(buttonNodes.length>0){
							submitNode = buttonNodes[0];
						}
						// or find any submit inputs in this fieldset instead
						else{
							inputNodes = submitNode.getElementsByTagName('INPUT');
							for(var a=0; a<inputNodes.length; a++) if(inputNodes[a].type=='submit' || inputNodes[a].type=='button') submitNode = inputNodes[a];
						}
						// post it
						void(rfu.clicked(submitNode));
						return true ;
					}
				}
			}

	// JQUERY WRAPPER
	if(typeof(jQuery)!='undefined'){
		(function($){
			var methods = {
				init : function(options) {
					return this.each(function(){
						classBehaviours.handlers.reloadFromUrl.start($(this).context);
					});
				}
			};
			$.fn.reloadFromUrl = function(method){
				if(methods[method]) {
					return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
				}else if(typeof method === 'object' || !method){
					return methods.init.apply(this, arguments);
				}else{
					$.error('Method ' +  method + ' does not exist on jQuery.reloadFromUrl');
				}
			};
		})(jQuery);

		// JQUERY EVENTS
		$(document).ready(function() {
			$(".reloadFromUrl").reloadFromUrl();
		});
	}

/*
	Source: classbehaviours.utilities.js
	ClassBehaviours is a javascript framework based on class-name parsing.
	Copyright 2011 by Maurice van Creij and published on http://www.woollymittens.nl/
	This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
*/

	// CLASSBEHAVIOURS CLASS
	// create the root classbehaviours object if it doesn't already exist
	if(typeof(classBehaviours)=='undefined') classBehaviours = {};

		// COMMON UTILITY FUNCTIONS
		// create the utilities child object if it doesn't already exist
		if(typeof(classBehaviours.utilities)=='undefined') classBehaviours.utilities = {
			// returns all nodes of the same class
			getElementsByClassName : function(className, node){
				// use the whole body if no target was provided
				var target = (node!=null) ? node : document ;
				// make an empty array for the results
				var foundNodes = new Array();
				// make a regular expression to recognise the class name
				var wantedClass = new RegExp('\\b'+className+'\\b');
				// 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 classname was found add it to the results
					if(wantedClass.test(allNodes[a].className)) foundNodes[foundNodes.length] = allNodes[a];
				}
				// return the list
				return foundNodes;
			},
			// gets the value of a parameter from a className
			getClassParameter : function(targetNode, paramName, defaultValue){
				if(targetNode!=null){
					var parameterValue = (targetNode.className.indexOf(' ' + paramName + '_')>-1) ?
						targetNode.className.split(' ' + paramName + '_')[1].split(' ')[0] :
						defaultValue ;
					return (isNaN(defaultValue) || defaultValue==null || defaultValue=='' || typeof(defaultValue)!='string') ?
						parameterValue :
						parseFloat(parameterValue.replace('D','.'));
				}else{
					return 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==null) targetNode.className = '';
					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(targetNode, count){
				var testNode = targetNode;
				if(count==null) count = 1;
				for(var a=0; a<count; a++){
					do {
						testNode = (testNode.nextSibling!=null) ? testNode.nextSibling : targetNode ;
					}while(testNode.nodeName.indexOf('#text')>-1);
				}
				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;
			},
			// find the parent node with the given classname
			findParentNode : 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;
			},
			// gracefully add an event handler
			addEvent : function(node, eventName, eventHandler){
				if('addEventListener' in node){
					node.addEventListener(eventName, eventHandler, false);
				}else if('attachEvent' in node){
					node.attachEvent('on'+eventName, function(event){eventHandler(event)});
				}else{
					node['on'+eventName] = eventHandler;
				}
				return true;
			},
			// trigger an event handler manually
			triggerEvent : function(node, eventName){
				if('fireEvent' in node){
					node.fireEvent('on' + eventName);
				}else if('dispatchEvent' in node){
					var evt = document.createEvent('HTMLEvents');
					evt.initEvent(eventName, false, true);
					node.dispatchEvent(evt);
				}else{
					eval('node.on' + eventName + '()');
				}
				return true;
			},
			// trim whitespace from around a string
			trim : function(string){
				var cu = classBehaviours.utilities;
				return cu.rtrim(cu.ltrim(string));
			},
			ltrim : function(string){
				var left = 0;
				while(left < string.length && string[left] == ' '){
					left++;
				}
				return string.substring(left, string.length);
			},
			rtrim : function(string){
				var right = string.length - 1;
				while(right > 0 && string[right] == ' '){
					right -= 1;
				}
				return string.substring(0, right + 1);
			}
		}

/*
	Source: classbehaviours.fader.js
	ClassBehaviours is a javascript framework based on class-name parsing.
	Copyright 2011 by Maurice van Creij and published on http://www.woollymittens.nl/
	This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
*/

	// CLASSBEHAVIOURS CLASS
	// create the root classbehaviours object if it doesn't already exist
	if(typeof(classBehaviours)=='undefined') classBehaviours = {};

		// COMMON FADER FUNCTIONS
		// create the utilities child object if it doesn't already exist
		if(typeof(classBehaviours.fader)=='undefined') classBehaviours.fader = {
			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.filters)!='undefined') if(typeof(node.filters.alpha)!='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 (isNaN(fadeValue)) ? null : 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.filters)!='undefined') if(typeof(node.filters.alpha)!='undefined') node.filters.alpha.opacity = amount;
//					if(typeof(node.style.filter)!='undefined') if(typeof(node.style.filter.alpha)!='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';
			},
			fade : function(id, start, end, step, delay, acceleration, evalOnEnd){
				var cf = classBehaviours.fader;
				// get the target node
				var target = (typeof(id)=='string') ? document.getElementById(id) : 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
				var timeOut;
				if(value!=end){
					timeOut = setTimeout(function(){
						classBehaviours.fader.fade(id, value, end, step+acceleration, delay, acceleration, evalOnEnd);
					}, delay);
				}else {
					if(typeof(evalOnEnd)=='string'){
						eval(evalOnEnd);
					}else{
						evalOnEnd();
					}
				}
				// return a way of cancelling the animation
				return timeOut;
			},
			size : function(id, start, end, step, delay, acceleration, evalOnEnd){
				var cf = classBehaviours.fader;
				// get the target node
				var target = (typeof(id)=='string') ? document.getElementById(id) : 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
				var timeOut;
				if(value!=end) {
					var nextTarget = target;
					var nextValue = value;
					var nextEnd = end;
					var nextStep = step+acceleration;
					var nextDelay = delay;
					var nextAcceleration = acceleration;
					var nextEvalOnEnd = evalOnEnd;
					timeOut = setTimeout(function(){
						classBehaviours.fader.size(nextTarget, nextValue, nextEnd, nextStep, nextDelay, nextAcceleration, nextEvalOnEnd);
					}, delay);
				}else {
					if(typeof(evalOnEnd)=='string'){
						eval(evalOnEnd);
					}else{
						evalOnEnd();
					}
				}
				// return a way of cancelling the animation
				return timeOut;
			},
			// START: legacy wrappers
			fadeIn : function(id, step, delay, evalOnEnd, acceleration){
				fade(id, 0, 100, step, delay, acceleration, evalOnEnd);
			},
			fadeOut : function(id, step, delay, evalOnEnd, acceleration){
				fade(id, 100, 0, step, delay, acceleration, evalOnEnd);
			},
			crossFade : function(idIn, idOut, amount, step, delay, evalOnEnd, acceleration){
				fade(idIn, 0, 100, step, delay, acceleration, evalOnEnd);
				fade(idOut, 100, 0, step, delay, acceleration, '');
			},
			grow : function(id, step, delay, evalOnEnd, acceleration){
				size(id, 1, null, step, delay, acceleration, evalOnEnd);
			},
			shrink : function(id, step, delay, evalOnEnd, acceleration){
				size(id, null, 1, step, delay, acceleration, evalOnEnd);
			}
			// END: legacy wrappers
		}

/*
	Source: classbehaviours.ajax.js
	ClassBehaviours is a javascript framework based on class-name parsing.
	Copyright 2011 by Maurice van Creij and published on http://www.woollymittens.nl/
	This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
*/

	// CLASSBEHAVIOURS CLASS
	// create the root classbehaviours object if it doesn't already exist
	if(typeof(classBehaviours)=='undefined') classBehaviours = {};

		// COMMON AJAX FUNCTIONS
		// this implementation of AJAX adds a request queue as a differentiating feature
		if(typeof(classBehaviours.ajax)=='undefined') classBehaviours.ajax = {
			queue : new Array(),
			deserializeHTML : function(text){
				newElement = document.createElement('DIV');
				newElement.innerHTML = text;
				return newElement;
			},
			deserializeXML : function(text){
				if (window.DOMParser){
					parser = new DOMParser();
					xmlDoc = parser.parseFromString(text,"text/xml");
				}else{
					xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
					xmlDoc.async = "false";
					xmlDoc.loadXML(text);
				}
				return xmlDoc;
			},
			defaultProgressHandler : function(progressStatus, progressNode, progressError){
				// fill the refered node with a progress report
				progressNode.innerHTML = (progressStatus>-1) ? 'Loading: ' + Math.round(progressStatus * 100) + '%' : 'Error: ' + progressError ;
			},
			defaultLoadHandler : function(loadXml, loadNode, loadText){
				// fill the refered node with the returned html string
				loadNode.innerHTML = loadText.split('<body>')[1].split('</body>')[0];
			},
			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=='default') ? this.defaultLoadHandler : loadHandler ;
				this.queue[index].doOnProgress	=	(progressHandler=='default') ? this.defaultProgressHandler : progressHandler ;
				this.queue[index].referObject	=	referingObject;
				// request properties
				this.queue[index].time			=	new Date();
				// 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 = 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]);
					}
				}
			},
			progress : function(){
				queued = 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(4, queued.referObject, queued.request.status, queued.time);
						// 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 = classBehaviours.ajax.deserializeHTML(queued.text)
						else if(queued.document.childNodes.length==0) queued.document = classBehaviours.ajax.deserializeHTML(queued.text);
						// trigger the load event
						if(queued.doOnLoad) queued.doOnLoad(queued.document, queued.referObject, queued.text, queued.time);
						// request the next item in the queue to be handled
						classBehaviours.ajax.handleQueue();
					}else{
						// update optional progress indicator code
						if(queued.doOnProgress) queued.doOnProgress(-1, queued.referObject, queued.request.status, queued.time);
					}
				}else{
					// update optional progress indicator code
					if(queued.doOnProgress) queued.doOnProgress(queued.request.readyState, queued.referObject, 200, queued.time);
				}
				// return the status if desired
				return queued.request.readyState;
			},
			wait : function(){
				queued = 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, queued.time);
				// else try again in a while
				}else{
					setTimeout("ajax.wait()",256);
				}
			},
			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;
				this.time			=	null;
			}
		}

/*
	Source: classbehaviours.parser.js
	ClassBehaviours is a javascript framework based on class-name parsing.
	Copyright 2011 by Maurice van Creij and published on http://www.woollymittens.nl/
	This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
*/

	// CLASSBEHAVIOURS CLASS
	// create the root classbehaviours object if it doesn't already exist
	if(typeof(classBehaviours)=='undefined') classBehaviours = {};

 		// COMMON PARSER FUNCTIONS
		// create the utilities child object if it doesn't already exist
		if(typeof(classBehaviours.parser)=='undefined') classBehaviours.parser = {
			// timeout constant
			interval : null,
			// verify the state of the object
			start : function(){
				var cp = classBehaviours.parser;
				// check if enough of the DOM was loaded
				if(/interactive|loaded|complete/i.test(document.readyState)){
					// cancel the interval
					clearInterval(cp.interval);
					// start the parser
					setTimeout(function(){cp.parseDocument()}, 100);
				// else if we're not already waiting for another test
				}else if(cp.interval==null){
					// test again
					cp.interval = setInterval(cp.start, 100);
				}
			},
			// scan the whole document
			parseDocument : function(){
				// pass the document object to the parser
				classBehaviours.parser.parseNode(document);
				// return the status
				return false;
			},
			// 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){
				// for all class behaviours
				var wantedClass;
				for(b in classBehaviours.handlers){
					// define the search based on the handler's expected classn ame
					wantedClass = new RegExp('\\b'+classBehaviours.handlers[b].name+'\\b');
					// if the behaviour name is in the className tested node
					if(wantedClass.test(node.className)){
						// apply its respective behaviour
						classBehaviours.handlers[b].start(node);
					}
				}
				// decide if to continue parsing deeper into this branch
				return (node.className.indexOf('doNotParse')<0);
			}
		}

		// initialise the parser manually if jQuery isn't used
		if(typeof(jQuery)=='undefined') classBehaviours.parser.start();

