
/*
	Source: classbehaviours.handlers.artificialscrollbar.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 = {}

			// Manages an artificial scroll bar
			classBehaviours.handlers.artificialScrollBar = {
				// properties
				name: 'artificialScrollBar',
				interaction: false,
				interactor: null,
				scrollInterval: null,
				index: 0,
				// methods
				start: function(node){
					// give the scrollbar an id if it doesn't have one
					node.id = (node.id) ? node.id : this.name + this.index++ ;
					// get the objects inside
					allDivs = node.getElementsByTagName('DIV');
					// disable the normal scrollbar
					node.style.overflow = 'hidden';
					// show the artificial scrollbar
					allDivs[allDivs.length-5].style.display = 'block';
					// set the canvas event handlers
					node.onmouseover = this.overCanvas;
					node.onmouseout = this.offCanvas;
					// set the detector events
					allDivs[allDivs.length-1].onmousedown = this.onDown;
					allDivs[allDivs.length-1].onmousemove = this.onDrag;
					allDivs[allDivs.length-1].ondrag = this.onDrag;
					document.onmouseup = this.onUp;
					node.onmousewheel = this.onWheel;
					if(window.addEventListener) window.addEventListener('DOMMouseScroll', this.onWheel, false);
					// set the button event handlers
					allDivs[allDivs.length-3].onmousedown = this.buttonDown;
					allDivs[allDivs.length-3].onmouseup = this.buttonOff;
					allDivs[allDivs.length-3].onmouseout = this.buttonOff;
					allDivs[allDivs.length-4].onmousedown = this.buttonUp;
					allDivs[allDivs.length-4].onmouseup = this.buttonOff;
					allDivs[allDivs.length-4].onmouseout = this.buttonOff;
					// start position
					setTimeout('classBehaviours.handlers.artificialScrollBar.moveTo(document.getElementById("'+node.id+'"), 0)', 100);
				},
				resize: function(rootNode){
					// get the required objects
					allDivs = rootNode.getElementsByTagName('DIV');
					contentNode = allDivs[0];
					indicatorNode = allDivs[allDivs.length-2];
				},
				moveTo: function(rootNode, position){
					// get the required objects
					allDivs = rootNode.getElementsByTagName('DIV');
					contentNode = allDivs[0];
					indicatorNode = allDivs[allDivs.length-2];
					scrollBarNode = allDivs[allDivs.length-5];
					// size the indicator
					contentToWindowRatio = contentNode.parentNode.offsetHeight / contentNode.offsetHeight;
					scrollBarHeight = indicatorNode.parentNode.offsetHeight;
					indicatorSize = contentToWindowRatio * scrollBarHeight;
					if(!isNaN(indicatorSize)) indicatorNode.style.height = Math.round(indicatorSize) + 'px';
					// hide the indicator when it's not needed
					scrollBarNode.style.visibility = (contentToWindowRatio>=1) ? 'hidden' : 'visible' ;
					// if the page is longer than the canvas
					if(contentToWindowRatio<1){
						// set the indicator position
						indicatorSurplus = indicatorSize / 2;
						indicatorPosition = position - indicatorSurplus;
						if(position < indicatorSurplus) indicatorPosition = 0;
						if(indicatorPosition > scrollBarHeight-indicatorSize) indicatorPosition = scrollBarHeight-indicatorSize;
						if(!isNaN(indicatorPosition)) indicatorNode.style.top = Math.round(indicatorPosition) + 'px';
						// set the content position
						scrollBarFraction = (position - indicatorSurplus) / (scrollBarHeight - indicatorSurplus * 2);
						contentOverlap = contentNode.offsetHeight - contentNode.parentNode.offsetHeight;
						contentPosition = scrollBarFraction * contentOverlap;
						if(contentPosition<0) contentPosition = 0;
						if(contentPosition>contentOverlap) contentPosition = contentOverlap;
						if(!isNaN(contentPosition)) contentNode.style.top = Math.round(0 - contentPosition) + "px";
					}
				},
				moveBy: function(rootNode, distance){
					if(rootNode){
						// get the required objects
						allDivs = rootNode.getElementsByTagName('DIV');
						contentNode = allDivs[0];
						indicatorNode = allDivs[allDivs.length-2];
						// get the current scroll position
						indicatorPosition = (indicatorNode.style.top) ? parseInt(indicatorNode.style.top) : 0 ;
						indicatorSize = (indicatorNode.style.height) ? parseInt(indicatorNode.style.height) : 0 ;
						// set the new scroll position
						this.moveTo(rootNode, indicatorPosition+indicatorSize/2+distance);
					}
				},
				// events
				onDown: function(that){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var asb = classBehaviours.handlers.artificialScrollBar;
					// activate the controls
					asb.interaction = true;
					// get the interaction location
					mouseY = (typeof(event)!='undefined' && navigator.userAgent.indexOf('Safari')<0) ? event.y : that.layerY ;
					// add a slight offset
					mouseY -= 10;
					// send the coordinates to the indicator
					asb.moveTo(objNode.parentNode.parentNode, mouseY);
					// cancel the click
					return false;
				},
				onDrag: function(that){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var asb = classBehaviours.handlers.artificialScrollBar;
					// if the controls are active
					if(asb.interaction){
						// get the interaction location
						mouseY = (typeof(event)!='undefined' && navigator.userAgent.indexOf('Safari')<0) ? event.y : that.layerY ;
						// add a slight offset
						mouseY -= 10;
						// send the coordinates to the indicator
						asb.moveTo(objNode.parentNode.parentNode, mouseY);
						// cancel the click
						return false;
					}
				},
				onUp: function(that){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var asb = classBehaviours.handlers.artificialScrollBar;
					// de-activate the controls
					asb.interaction = false;
					// cancel the click
					return false;
				},
				buttonUp: function(that){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var asb = classBehaviours.handlers.artificialScrollBar;
					// move the scroller 1 step up
					asb.moveBy(asb.interactor, -5);
					// set an interval
					asb.scrollInterval = setInterval("classBehaviours.handlers.artificialScrollBar.moveBy(classBehaviours.handlers.artificialScrollBar.interactor, -5)", 50);
					// cancel the click
					return false;
				},
				buttonDown: function(that){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var asb = classBehaviours.handlers.artificialScrollBar;
					// move the scroller 1 step down
					asb.moveBy(asb.interactor, 5);
					// set an interval
					asb.scrollInterval = setInterval("classBehaviours.handlers.artificialScrollBar.moveBy(classBehaviours.handlers.artificialScrollBar.interactor, 5)", 50);
					// cancel the click
					return false;
				},
				buttonOff: function(that){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var asb = classBehaviours.handlers.artificialScrollBar;
					// clear the scrolling interval
					clearInterval(asb.scrollInterval);
					// cancel the click
					return false;
				},
				onWheel: function(that){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var asb = classBehaviours.handlers.artificialScrollBar;
					// get the scroll distance
					distance = (window.event) ? window.event.wheelDelta/120 : -objNode.detail/3 ;
					// scroll the page
					asb.moveBy(asb.interactor, -5*distance);
					// cancel the click
					return false;
				},
				overCanvas: function(that){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var asb = classBehaviours.handlers.artificialScrollBar;
					// store the object hovered over
					asb.interactor = objNode;
				},
				offCanvas: function(that){
					var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
					var asb = classBehaviours.handlers.artificialScrollBar;
					// store the object hovered over
					asb.interactor = null;
				}
			}

	// JQUERY WRAPPER
	if(typeof(jQuery)!='undefined'){
		(function($){
			var methods = {
				init : function(options) {
					return this.each(function(){
						classBehaviours.handlers.artificialScrollBar.start($(this).context);
					});
				}
			};
			$.fn.artificialScrollBar = 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.artificialScrollBar');
				}
			};
		})(jQuery);

		// JQUERY EVENTS
		$(document).ready(function() {
			$(".artificialScrollBar").artificialScrollBar();
		});
	}

/*
	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.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();

