/*
Section: Global Functions
	
Overview:
	The master script file, contains global classes / functions / methods etc. 

Authors:
	- Ryan Marsh <ryan@fsite.com>
	- Chris Martin <chris@fsite.com>
*/

/*
Namespace: Page
	Root namespace for global page initialization logic
*/

var Page = {
	/*
	Constructor: Init
		Invokes related methods of this object
	*/
	Init: function(){
		Page.bindScrolls();
		Page.InitTips();
	},
	
	/*
	Method: bindTableStylesGrid
		Sets hover/blur effects on table rows whos table class name contains 'tablestyles'
	*/
	bindTableStylesGrid: function(){
		$$( '.tablestyles' ).each( function( table ) {
			if( !table.hasClass( 'noanim' ) ){
				var rows = table.getElements( 'tbody' )[0].getElements( 'tr' );
				rows.each( function( row ) {
					row.addEvent( 'mouseover', function(){ this.addClass( 'tr-highlight' ); } );
					row.addEvent( 'mouseout', function(){ this.removeClass( 'tr-highlight' ); } );
				});
			}
		});
	},
	
	/*
	Method: bindTrainingGrid
		Sets hover/blur effects on training grids
	*/
	bindTrainingGrid: function(){
		var trows = document.getElementsByTagName('div');
		for( var i = 0; i < trows.length; i++ ){
			var trowclass = trows[i].className;
			
			if( trowclass == 'training-one') {
				trows[i].onmouseover = function() {
					this.className += ' training-highlight ';
				}
				trows[i].onmouseout = function() {
					this.className = this.className.replace('training-highlight', '');
				}
			}
			
			if( trowclass == 'training-two' ){
				trows[i].onmouseover = function(){
					this.className += ' training-highlight ';
				}
				trows[i].onmouseout = function(){
					this.className = this.className.replace('training-highlight', '');
				}
			}
		}
	},
	
	/*
	Method: bindWebLinks
		Sets hover/blur effects on web links on category course list pages
	*/
	bindWebLinks: function(){
		$$( '.useful-links' ).each( function( link ){
			link.addEvent( 'mouseover', function( e ){
				this.className = this.className.replace( 'useful-links', 'useful-link-details' );
			});
			
			link.addEvent( 'mouseout', function( e ){
				this.className = this.className.replace( 'useful-link-details', 'useful-links' );
			});
		});
		
		$$( '.useful-links-alternate' ).each( function( link ){
			link.addEvent( 'mouseover', function( e ){
				this.className = this.className.replace( 'useful-links-alternate', 'useful-link-details-alternate' );
			});
			
			link.addEvent( 'mouseout', function( e ){
				this.className = this.className.replace( 'useful-link-details-alternate', 'useful-links-alternate' );
			});
		});
	},
	
	/*
	Method: bindScrolls
		Initializes page scrolling effects. All links on the page with i.e. #top will have scrolling effect added
	*/
	bindScrolls: function(){
		//window._smoothScroll = new SmoothScroll();
	},
	
	/*
	Method: InitTips
		Configures the specified elements for mootools tip
	*/
	InitTips: function( elems )
	{
		if( !$defined( elems ) ) elems = $$('.tooltip');
		
		new Tips( elems, { 
			fixed: false, 
			offsets: { 'x': -12, 'y': -4 },
			initialize:function(){
				this.fx = new Fx.Style( this.toolTip, 'opacity', { 
					duration: 50, 
					transition: Fx.Transitions.Quart.easeOut
				} ).set( 0 );
			},
			onShow: function( toolTip ) {
				this.fx.set( 1 );
			},
			onHide: function( toolTip ) {
				this.fx.set( 0 );
			}
		});
	}
};
window.addEvent( 'load', Page.Init );

/*
Namespace: XHRStatus
	Most Ajax requests return a status code which maps to the codes in this function. additional information about
	these codes are provided here for user feedback etc.
*/
var XHRStatus = {
	/*
	Property: Codes
		A collection of xhr response codes and messages
	*/
	Codes: [
		{ Code:  0, Name: "Ok",  FriendlyMessage: "" },
		{ Code: -1, Name: "HandledException",  FriendlyMessage: ""  },
		{ Code: -2, Name: "UnhandledException",  FriendlyMessage: ""  },
		{ Code: -3, Name: "AuthenticationRequiredException",  FriendlyMessage: ""  },
		{ Code: -4, Name: "InvalidParameters",  FriendlyMessage: ""  }
	],
	
	/*
	Method: GetStatus
		Returns the code from XHRStatus.Codes
		
	Arguments:
		code - The input code to locate
	*/
	GetStatus: function( code ){
		for( var i = 0; i < XHRStatus.Codes.length; i++ ){
			if( XHRStatus.Codes[i].Code == code )
				return XHRStatus.Codes[i];
		}
	}
};

/*
Namespace: ErrorReport
	All validation functions throughout the site should pass an array of error messages to this function
	to display to the user.
*/

var ErrorReport = {
	/*
	Method: Build
		Returns formated message text to be shown to the user
		
	Arguments:
		errors - The array containing the errors messages
	*/
	Build: function( errors ){
		if( $defined( errors ) && errors.length > 0 ){
			var errorsString = "";
			for( var i = 0; i < errors.length; i++ ) errorsString += " - " + errors[i] + '\n';
			
			return( "Please amend the following problems:\n\n" + errorsString );
		}
		return;
	},
	
	/*
	Method: Show
		Calls <ErrorReport.Build> to create the message text and displays the message to the user
		
	Arguments:
		errors - The array containing the errors messages
	*/
	Show: function( errors ){
		var report = ErrorReport.Build( errors );
		if( $defined( errors ) )
			alert( report );
	}
};

/*
Class: MessageBoxClass
	Global helper class for displaying messages to the user. 
*/

var MessageBox;
var MessageBoxClass = new Class({
	/*
	Constructor: initialize
		Initializes the class and sets default values
	*/
	initialize: function(){
		if( $('MessageBox') ){
			this.Container = $('MessageBox');
			this.ListContainer = this.Container.getElements( 'ul' )[0];
			
			this.Container.setOpacity( 0 );
			this.ContainerEffect = this.Container.effect( 'opacity', { duration: 400, transition:  Fx.Transitions.Quart.easeOut });
		}
	},
	
	/*
	Method: Show
		Instaniates a new <MessageBoxItemClass> and passes the object to <addItem>
		
	Arguments:
		message - The message to add to the message box
	*/
	Show: function( message ){
		this.addItem( new MessageBoxItemClass( message ) );
	},
	
	/*
	Method: addItem
		Called from <Show>. Adds the new message to the messbox
		
	Arguments:
		item - The <MessageBoxItemClass> object to add to the message box list
	*/
	addItem: function( item ){
		this.ListContainer.appendChild( item.Node );
		this.showMessageBox();
		item.StartTimer();
	},
	
	/*
	Method: Update
		Evaluates the message box current state. Hides the box if no messages are present.
	*/
	Update: function(){
		if( this.getMessageCount() == 1 ){
			this.hideMessageBox();
		}
	},
	
	/*
	Method: showMessageBox
		Shows the messagebox to the user
	*/
	showMessageBox: function(){
		this.ContainerEffect.start( 0, 1.0 );
		this.Container.style.display = 'block';
	},
	
	/*
	Method: hideMessageBox
		Hides the message box
	*/
	hideMessageBox: function(){
		this.ContainerEffect.start( 0.7, 0 );
	},
	
	/*
	Method: ShowError
		Shows the error message type
		
	Arguments:
		message - The error message to show. If no message is defined the generic message is shown.
	*/
	ShowError: function( message ){
		if( !$defined( message ) )
			message =  Messages.Get( 'general', 'G01' );
			
		alert( message );
	},
	
	/*
	Method: getMessageCount
		Returns the number of messages currently in the list
	*/
	getMessageCount: function(){
		return this.ListContainer.getElements( 'li' ).length;
	}
});

/*
Class: MessageBoxItemClass
	Creates a new message and the appropraite HTML elements for adding to the <MessageBoxClass> message list.
	Handles removing of messages from the list after a set interval [5000].
*/
var MessageBoxItemClass = new Class({
	/*
	Constructor: initialize
	*/
	initialize: function( message ){
		this.message = message;
		this.Node = new Element( 'li' );
		this.Node.setHTML( message );
	},
	
	/*
	Method: StartTimer
		Stats timeout to remove the item from the message list
	*/
	StartTimer: function(){
		this.remove.delay( 5000, this );
	},
	
	/*
	Method: remove
		Remove the current item from the list
	*/
	remove: function(){
		this.Node.parentNode.removeChild( this.Node );
		MessageBox.Update();
	}
});

window.addEvent( 'load', function(){ 
	if( $('MessageBox') )
		MessageBox = new MessageBoxClass();
});

/*
Class: DataGrid 
	Providers static helper methods for data grids throughout the site
*/

var DataGrid = {
	/*
	Method: BindAlternatingStyle 
		Iterates through the specified table rows and sets the alternate class name. 
		Mainly used when removing rows client side to reset the styling.
		
	Arguments:
		container - Container item to select target rows
		rowTagName - Grid row tagname, the css will be applied to this
		className - The css class to apply to the non alternate rows
		alternateClassName - The css class to apply to the alternate rows
	*/
	BindAlternatingStyle: function( container, rowTagName, className, alternateClassName ){
		if( $defined( $( container ) ) ){
			var rows = $( container ).getElements( rowTagName );
			var index = 0;
			
			rows.each( function( row, i ){
				if( row.getElements( 'th' ).length == 0 ){
					if( index % 2 != 0 ){
						row.className = alternateClassName;
					}
					else{
						row.className = className;
					}
					
					index++;
				}
			});
		}
	},
	
	/*
	Method: ApplyAlternateStyles 
		Iterates through the specified elements and sets class and alternate class values
		
	Arguments:
		elems - $$ elements to iterate
		class - The css class to apply to the non alternate elements
		alternateClass - The css class to apply to the alternate elements
	*/
	ApplyAlternateStyles: function( elems, className, alternateClass ){
		var index = 0;
		( $type( elems ) == 'string' ? $$( elems ) : elems ).each( function( row, i ){
			if( $defined( className ) ) row.removeClass( className );
			if( $defined( alternateClass ) ) row.removeClass( alternateClass );
		
			if( index.isEven() ){
				if( $defined( className ) ) row.addClass( className )
			}
			else {
				if( $defined( alternateClass ) ) row.addClass( alternateClass );
			} 
			
			index++;
		});
	}
};

/*
Class: Toggler 
	Creates +- generic toggler for mootools slide class
*/

var SlideToggler = new Class({
	/*
	Constructor: initialize
		Initializes the class and sets default values
		
	Arguments:
		container - The element to inject to the toggler HTML into
		visible - The current status for the slide to toggle.
		slide -	The slide object to work with
		
	Example:
		>var mySlideToggler = new SlideToggler( 'myTabletId', true, 'mySlideObject' );
		>mySlideToggler.Toggle();
	*/
	initialize: function( container, visible, slide ){
		this.container = $(container);
		this.visible = $defined( visible ) ? visible : false;
		this.Slide = slide;
		this.loadMarkup();
		this.cookieKey = container;
		
		var cookieValue = this.getCookie();
		
		if( !cookieValue ){
			cookieValue = visible ? '1' : '0';
			this.setCookie( cookieValue );
		}
		
		if( cookieValue == '1' && !visible ) this.Activate();
		if( cookieValue == '0' && visible ) this.Deactivate();
	},
	
	/*
	Method: Toggle
		Alternates the current status of the slide
	*/
	Toggle: function(){
		this.visible ? this.Deactivate() : this.Activate();
	},
	
	/*
	Method: Activate
		Show the slide
	*/
	Activate: function(){
		this.visible = true;
		this.Slide.stop().slideIn();
		this.setCookie( '1' );
	
		this.textAnchor.setHTML( "Less" );
		this.imageElement.src = this.imageElement.src.replace( 'plus', 'minus' );
	},
	
	/*
	Method: Deactivate
		Hide the slide
	*/
	Deactivate: function(){
		this.Slide.stop().slideOut();
		this.visible = false;
		this.setCookie( '0' );

		this.textAnchor.setHTML( "More" );
		this.imageElement.src = this.imageElement.src.replace( 'minus', 'plus' );
	},
	
	/*
	Method: loadMarkup
		Create the HTML for the toggler
	*/
	loadMarkup: function(){
		this.textAnchor = new Element( 'a', { 'href': 'javascript:void(0)', 'styles': { 'margin-right': '5px' } } );
		this.textAnchor.setHTML( this.visible ? "Less" : "More" );
		this.textAnchor.addEvent( 'click', this.Toggle.bindAsEventListener(this) );
		
		this.imageAnchor = new Element( 'a', { 'href': 'javascript:void(0)' } );
		this.imageElement = new Element( 'img', { 'src': '/Assets/Images/Common/' + ( this.visible ? 'minus' : 'plus' ) + '_sign.gif' } );
		this.imageAnchor.appendChild( this.imageElement );
		this.imageAnchor.addEvent( 'click', this.Toggle.bindAsEventListener(this) );
		
		this.container.appendChild( this.textAnchor );
		this.container.appendChild( this.imageAnchor );
	},
	
	setCookie: function( v ){
		Cookie.set( this.cookieKey, v, { duration: 256 } );
	},
	
	getCookie: function(){
		return Cookie.get( this.cookieKey );
	}
});


/*
Class: PopWindow
	Provides additional functionaility to the standard window.open() function
*/

var PopWindow = new Class({
	/*
	Constructor: initialize
		Instantiate a new instance of this object
		
	Arguments:
		url - The URL for the contents of the window.
		title - Title to be assigned to the window
		width - Initial width of the window
		height - Initial height of the window
		features - Custom features for the window
		linkid - unique id of refering A href.
		
	Example:
		>var myPop = new PopWindow( 'http://www.google.com', 'MyTitle', 500, 500, 'scrollbars=1, status=1', 'mod689' );
		>myPop.Open();
	*/
	initialize: function( url, title, width, height, features, linkid ){
		if( width == 0 ) width = screen.width;
		if( height == 0 ) height = screen.height;
		
		this.Url = url;
		this.Title = title;
		this.Width = width;
		this.Height = height;
		
		$(linkid).href = 'javascript:void(0)';
		
		this.Left = ( screen.width - width ) / 2;
		this.Top = ( screen.height - height ) / 2;
		
		this.Features = ( features + "" ).length > 0 ? features + ',' : '';
		this.Features += 'left=' + this.Left + ', top=' + this.Top + ', status = 1';
	},
	
	/*
	Method: Open
		Open the window
	*/
	Open: function(){
		this.Window = Window.open( 
			this.Url, 
			this.Title, 
			this.Features + ( ( this.Features != '' ) ? ',':'' ) + 'width=' + this.Width + ',height=' + this.Height 
			);
	    this.Window.focus();
	},
	
	/*
	Method: Close
		Closes the window
	*/
	Close: function(){
		this.Window.close();
	}
});

/*
Class: Fx.ScrollWindow
	Enable scrolling of the window object to the specified positions.
*/

Fx.ScrollWindow = Fx.Base.extend({
	/*
	Constructor: initialize
	*/
	initialize: function( options ){ 
		this.setOptions( options ); 
		this.now = []; 
		var b = document.body; 
		var stop = this.clearTimer.bind(this); 
		
		if( b.addEventListener )
			b.addEventListener( 'DOMMouseScroll', stop, false ); 
		else 
			b.onmousewheel = stop; 
	}, 
	
	/*
	Method: setNow
	*/
	setNow: function(){
		[0, 1].each( function(i){ 
			this.now[i] = this.compute( this.from[i], this.to[i] ); 
		}, this ); 
	}, 
	
	/*
	Method: scrollTo
		Scroll the window to the specified position
	
	Arguments:
		x - Page X position to scroll to
		y - Page Y position to scroll to
	*/
	scrollTo: function( x, y ){
		if( null == x && null == y ){
			x = this.prevPos.x;
			y = this.prevPos.y;
		}
		else {
			var pos = this.getScroll();
			this.prevPos.x = pos.x;
			this.prevPos.y = pos.y;
		}
	
		if( this.timer && this.options.wait ) return; 
		var left = Window.getScrollLeft(); 
		var top = Window.getScrollTop();
		var width = Window.getWidth(); 
		var height = Window.getHeight(); 
		var fullWidth = Window.getScrollWidth(); 
		var fullHeight = Window.getScrollHeight(); 
		var maxScrollWidth = fullWidth - width; 
		var maxScrollHeight = fullHeight - height; 
		
		if( x > maxScrollWidth ) x = maxScrollWidth; 
		if( y > maxScrollHeight ) y = maxScrollHeight; 
		
		return this.custom( [left, top], [x, y] ); 
	},
	
	/*
	Method: toElement
		Scroll the window to the specified element
	
	Arguments:
		el - $element or the ID of the element to scroll to
	*/
	toElement: function( el ){ 
		return this.scrollTo( $Element( el, 'getLeft' ), $Element( el, 'getTop' ) ); 
	}, 
	
	/*
	Method: increase
	*/
	increase: function(){ 
		window.scrollTo( this.now[0], this.now[1] ); 
	},
	
	/*
	Method: scrollToPrevPos
		Scroll the window to the previous scroll position
	*/
	scrollToPrevPos: function(){
		this.scrollTo();
	},
	
	/*
	Property: prevPos
		The x and y coordinates of the previous scroll position
	*/
	prevPos: { 
		x: 0, 
		y: 0 
	},
	
	/*
	Method: getScroll
		Get safe page scroll x, y coordinates. 
	*/
	getScroll: function(){
		var scrOfX = 0, scrOfY = 0;
		if( typeof( window.pageYOffset ) == 'number' ) { //Netscape compliant
			scrOfY = window.pageYOffset; scrOfX = window.pageXOffset;
		} 
		else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) { //DOM compliant
			scrOfY = document.body.scrollTop; scrOfX = document.body.scrollLeft;
		} 
		else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) { //IE6 standards compliant mode
			scrOfY = document.documentElement.scrollTop; scrOfX = document.documentElement.scrollLeft;
		}
		return { x: scrOfX, y: scrOfY };
	}
});

/*
Class: SmoothScroll
	Auto targets all the anchors in a page and display a smooth scrolling effect upon clicking them.
	Inherits methods, properties, options and events from <Fx.Scroll>.

Note:
	SmoothScroll requires an XHTML doctype.

Arguments:
	options - the Fx.Scroll options (see: <Fx.Scroll>) plus links, a collection of elements you want your smoothscroll on. Defaults to document.links.

Example:
	>new SmoothScroll();
*/

var SmoothScroll = Fx.Scroll.extend({
	/*
	Constructor: initialize
		initializes an instance of hte current class and parse the either the input querystring or the document querystring
		
	Arguments:
		options - Configuration options for the class
	*/
	initialize: function(options){
		this.parent(window, options);
		this.links = (this.options.links) ? $$(this.options.links) : $$(document.links);
		var location = window.location.href.match(/^[^#]*/)[0] + '#';
		this.links.each(function(link){
			if (link.href.indexOf(location) != 0) return;
			var anchor = link.href.substr(location.length);
			if (anchor && $(anchor)) this.useLink(link, anchor);
		}, this);
		if (!window.webkit419) this.addEvent('onComplete', function(){
			window.location.hash = this.anchor;
		});
	},

	/*
	Method: useLink
		Wireup events for the specified anchor
		
	Arguments:
		link - The anchor to scroll to
		anchor - The anchor tag to wire the event to
	*/
	useLink: function(link, anchor){
		link.addEvent('click', function(event){
			this.anchor = anchor;
			this.toElement(anchor);
			event.stop();
		}.bindWithEvent(this));
	},
	
	/*
	Method: toElement
		Scroll to the specified element
		
	Arguments:
		el - The element to scroll to
	*/
	toElement: function( el ){
		var parent = this.element.getPosition(this.options.overflown);
		var target = $(el).getPosition(this.options.overflown);
		return this.scrollTo( 0, target.y - parent.y );
	}
});

/*
Class: QueryString
	Handles parsing of the QueryString formated parameters
*/

var QueryString = new Class({
	/*
	Constructor: initialize
		initializes an instance of hte current class and parse the either the input querystring or the document querystring
		
	Arguments:
		qs - QueryString formated parameters. If not passed will get values from the request QueryString 
	*/
	initialize: function( qs ){
		this.params = new Object();
		
		if( qs == null )
			qs = location.search.substring( 1, location.search.length );

		if( qs.length == 0 ) return

		qs = qs.replace(/\+/g, ' ');
		var args = qs.split( '&' );
		
		for( var i = 0; i < args.length; i++ ){
			var value;
			var pair = args[i].split( '=' );
			var name = unescape( pair[0] );

			if( pair.length == 2 )
				value = unescape( pair[1] );
			else
				value = name
			
			this.params[name] = value
		}
	},
	
	/*
	Method: Get
		After the class has been instantiated can use this method to get a parameter
		
	Arguments:
		key - parameter name as in the query string
		d - default value if parameter is not present
	*/
	Get: function( key, d ){
		var value = this.params[key];
		
		if( null == value ) 
			value = d;
		
		return value
	}
});

/*
Class: Element
	Extending element object
*/

Element.extend({
	/*
	Method: hide
		Set element style.display value to 'none'
	*/
	hide: function(){
		this.setStyle( 'display', 'none' );
		return this;
	},
	
	/*
	Method: show
		Set element style.display value to 'block'
	*/
	show: function(){
		this.setStyle( 'display', 'block' );
		return this;
	},
	
	/*
	Method: show
		Set element style.display value to ''
	*/
	showBlank: function(){
		this.setStyle( 'display', '' );
		return this;
	},
	
	/*
	Method: getParent
		Get the elements parent element that matches the argument 'tagName'
		
	Arguments:
		tagName - The parent element tagname to return
	*/
	getParent: function( tagName ){
		var node = this.parentNode, i = 0;
		while( node && i++ < 100 ){
			if( node.nodeName.toLowerCase() == tagName.toLowerCase() ) return $(node);
		}
	}
});

/*
Class: Number
	Extending number object
*/
Number.prototype.isEven = function(){
	return this % 2 == 0;
};
	
Number.prototype.isOdd = function(){
	return this % 2 != 0;
};


/*
Class: Ajax
	Extends the Ajax class to add global event handlers
*/
Ajax = Ajax.extend(
{
	/*
	Constructor: initialize
		Initializes the base Ajax class and assigns event handlers
	*/
	initialize: function(url, options) 
	{
		this.parent(url, options);
		
		this.addEvent( 'onRequest', function(){ 
			$(document.body).setStyle( 'cursor', 'wait' ) 
		});
		
		this.addEvent( 'onComplete', function(){ 
			$(document.body).setStyle( 'cursor', 'default' ) 
		});
		
		this.addEvent( 'onFailure', function(){ 
			$(document.body).setStyle( 'cursor', 'default' );
			alert( Messages.Get( 'general', 'XHR01' ).Message );
		});
	}
});

/*
Class: Tips
	Extends the Mootools Tips class
*/
var Tips = Tips.extend({
	/*
	Constructor: initialize
		Initialize the base class and set default options
		
	Arguments:
		elements - The elements to load the tip object onto
		options - Configuration options
	*/
	initialize: function(elements, options){
		this.parent(elements, options);
	},

	/*
	Method: position
		Postition the tip
		
	Arguments: 
		element - The element to place the tip
	*/
	position: function(element){
		var pos = element.getPosition();
		this.toolTip.setStyles({
			'left': pos.x + this.options.offsets.x,
			'top': pos.y + this.options.offsets.y 
		});
	},

	/*
	Method: locate
		Get tip positioning
		
	Arguments: 
		event - Browser event
	*/
	locate: function(event){
		var win = {'x': window.getWidth(), 'y': window.getHeight()};
		var scroll = {'x': window.getScrollLeft(), 'y': window.getScrollTop() };
		var tip = {'x': this.toolTip.offsetWidth, 'y': this.toolTip.offsetHeight};
		var prop = {'x': 'left', 'y': 'top'};
		for (var z in prop){
			var pos = event.page[z] + this.options.offsets[z];
			//if( ( pos + tip[z] - scroll[z] ) > win[z] ) pos = event.page[z] - this.options.offsets[z] - tip[z];
			this.toolTip.setStyle( prop[z], pos - ( z == 'y' ? this.toolTip.offsetHeight : 0 ) );
		};
	},
	
	/*
	Method: build
		Constructs the tooltip object title / text etc
		
	Arguments:
		el - The element to apply the tip to
	*/
	build: function(el){
		//el.$tmp.myTitle = (el.href && el.getTag() == 'a') ? el.href.replace('http://', '') : (el.rel || false);
		el.$tmp.myTitle = "";
		if (el.title){
			var dual = el.title.split('::');
			if (dual.length > 1){
				el.$tmp.myTitle = dual[0].trim();
				el.$tmp.myText = dual[1].trim();
			} else {
				el.$tmp.myText = el.title;
			}
			el.removeAttribute('title');
		} else {
			el.$tmp.myText = false;
		}
		if (el.$tmp.myTitle && el.$tmp.myTitle.length > this.options.maxTitleChars) el.$tmp.myTitle = el.$tmp.myTitle.substr(0, this.options.maxTitleChars - 1) + "&hellip;";
		el.addEvent('mouseenter', function(event){
			this.start(el);
			if (!this.options.fixed) this.locate(event);
			else this.position(el);
		}.bind(this));
		if (!this.options.fixed) el.addEvent('mousemove', this.locate.bindWithEvent(this));
		var end = this.end.bind(this);
		el.addEvent('mouseleave', end);
		el.addEvent('trash', end);
	},
	
	/*
	Method: start
		Event fired on mouse over
		
	Arguments:
		el - The element to apply the tip to
	*/
	start: function(el){
		this.wrapper.empty();
		//if (el.$tmp.myTitle){
			this.title = new Element('span').inject(new Element('div', {'class': this.options.className + '-title'}).inject(this.wrapper)).setHTML(el.$tmp.myTitle);
		//}
		if (el.$tmp.myText){
			this.text = new Element('span').inject(new Element('div', {'class': this.options.className + '-text'}).inject(this.wrapper)).setHTML(el.$tmp.myText);
		}
		$clear(this.timer);
		this.timer = this.show.delay(this.options.showDelay, this);
	}
});



/*
Section: getElementsByClassName, hideItems
	Various UI manipulation: functions to be replaced
*/

function getElementsByClassName(oElm, strTagName, strClassName){
	var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
	var arrReturnElements = new Array();
	strClassName = strClassName.replace(/-/g, "\-");
	var oRegExp = new RegExp("(^|\s)" + strClassName + "(\s|$)");
	var oElement;
	for(var i=0; i<arrElements.length; i++){
		oElement = arrElements[i];
		if(oRegExp.test(oElement.className)){
			arrReturnElements.push(oElement);
		}
	}
	return (arrReturnElements)
}

function hideItems(id, tag, className) {
	var els = getElementsByClassName(document, tag, className);
	for(var i = 0; i<els.length; i++) {
		els[i].style.display = els[i].id == id ? "block" : "none";
	}
	return(els);
}