/*
	FILENAME: jsweld.animation.js
	AUTHOR: Philip Siedow-Thompson (psiedow@co.weld.co.us)
	WRITTEN: 4/2/2009
	REVISION: 7/17/2009
	DEPENDENCIES: prototype.js v.1.6.0 - (DO NOT USE ANY OTHER VERSION UNTIL TESTED!)
				  jsweld.wcDOM
				  jsweld.collections
				  jsweld.ajax
*/


		/* 
			FUNCTION:
			DESCRIPTION:
			ARGUMENTS:
			RETURNS:
		*/

var jsweld;
	jsweld = (jsweld)?jsweld:{};
	jsweld.calendar = {};
	
	/*------------------------------------------------------------------------------------
		JSWELD.CALENDAR.CONSTANTS - calendar constants.
	--------------------------------------------------------------------------------------*/
	jsweld.calendar.constants = {};
		jsweld.calendar.constants.DAYSPERMONTH = [31,28,31,30,31,30,31,31,30,31,30,31];
		jsweld.calendar.constants.LEAPDAYSPERMONTH = [31,29,31,30,31,30,31,31,30,31,30,31];
		jsweld.calendar.constants.MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
	
	/*------------------------------------------------------------------------------------
		JSWELD.CALENDAR.UTIL - utility functions.
	--------------------------------------------------------------------------------------*/
	jsweld.calendar.util = {};
		/* 
			FUNCTION: isLeapYear
			DESCRIPTION: returns true if the year specified is a leap year.
			ARGUMENTS: a_year - year (NUMBER)
			RETURNS: boolean
		*/
		jsweld.calendar.util.isLeapYear = function(a_year)
		{
			return (((a_year%4==0)&&(a_year%100!=0))||(a_year%400==0));	
		}
	
	/*------------------------------------------------------------------------------------
		JSWELD.CALENDAR.CALENDARBASE - base class of all calendars.
	--------------------------------------------------------------------------------------*/
	jsweld.calendar.basecalendar = Class.create(
	{
		/* 
			FUNCTION: constructor
			DESCRIPTION: base constructor
			ARGUMENTS: a_node - the node that will contain the calendar. THE NODE IS CLEARED OF ALL CHILDREN!
		*/
		initialize: function(a_node)
		{
			this.node = a_node;
			this.allowPrevious = true;						
			this.today = new Date();
			this._year = null;
			this._month = null;
			
			this.displayDate(this.today.getMonth(),this.today.getFullYear());
		},
		
		/* 
			FUNCTION: displayDate
			DESCRIPTION: displays the month and year selected
			ARGUMENTS: a_month - the month [Number 0-9]
						a_year - the year
			RETURNS: void
		*/
		displayDate: function(a_month,a_year)
		{
			var thisRef = this;
			var year = (a_year!=null)?a_year:this._year;
			var month = (a_month!=null)?a_month:this._month;
										
			while(month>=12)
			{
				month = month-12;
				year++;
			}
			while(month < 0)
			{
				month = month + 12;
				year--;
			}
			
			this._year = year;
			this._month = month;
			
			var isLeapYear = jsweld.calendar.util.isLeapYear(year);
			var numberOfDays = (isLeapYear)?jsweld.calendar.constants.LEAPDAYSPERMONTH[month]:jsweld.calendar.constants.DAYSPERMONTH[month];
			var firstDayOfMonth = new Date(year,month,1).getDay();
			var prevMonth = (month>0)?month-1:11;
			var prevNumberOfDays = (isLeapYear)?jsweld.calendar.constants.LEAPDAYSPERMONTH[prevMonth]:jsweld.calendar.constants.DAYSPERMONTH[prevMonth];
	
			var dayNumber = 1;
			var prevMonthDayNumber = prevNumberOfDays - firstDayOfMonth+1;
			var nextMonthDayNumber = 1;
																		
			var calendarHead = jsweld.wcDOM.createFromObject({element:'thead', children:[
															{element:'tr', children:[
																{element:'th', children:[{element:'a', text:'<', attributes:{href:'#', onclick:function(){thisRef.displayDate(month-1,year); return false;}}}]},
																{element:'th', text:jsweld.calendar.constants.MONTHS[month] + ' ' + year, attributes:{colSpan:5}},
																{element:'th', children:[{element:'a', text:'>', attributes:{href:'#', onclick:function(){thisRef.displayDate(month+1,year); return false;}}}]}
																]},
															{element:'tr', children:[{element:'th', text:'S'},
																					 {element:'th', text:'M'},
																					 {element:'th', text:'T'},
																					 {element:'th', text:'W'},
																					 {element:'th', text:'T'},
																					 {element:'th', text:'F'},
																					 {element:'th', text:'S'},
																					 ]}
															]});
			
			var calendarBody = jsweld.wcDOM.createFromObject({element:'tbody'});
			var hasStarted = false;
										
			while(dayNumber <= numberOfDays)
			{
				var row = calendarBody.appendChild(jsweld.wcDOM.createFromObject({element:'tr'}));
			
				for(var i=0; i<7; i++)
				{
					var isToday = (year==this.today.getFullYear() && month==this.today.getMonth() && dayNumber==this.today.getDate()); 
					var isBeforeToday = false;
					if(year<this.today.getFullYear())isBeforeToday = true;
						else if(year==this.today.getFullYear() && month<this.today.getMonth())isBeforeToday = true;
							else if(year==this.today.getFullYear() && month==this.today.getMonth() && dayNumber<this.today.getDate())isBeforeToday = true;
					
					if(firstDayOfMonth==i)hasStarted = true;
					if(hasStarted && dayNumber<=numberOfDays)
					{
						var eventClass = (this.hasEventOnDay(new Date(year,month,dayNumber)))?'jsweld_calendar_event':'';
						if(!isToday)
						{
							if(!isBeforeToday||this.allowPrevious)row.appendChild(jsweld.wcDOM.createFromObject({element:'td', attributes:{className:'jsweld_calendar_thisMonth'}, children:[{element:'a', text:dayNumber, attributes:{className:eventClass, href:'#', onclick:function(){thisRef.onDateSelect(new Date(year,month,this.firstChild.nodeValue)); return false;}}}]}));
								else row.appendChild(jsweld.wcDOM.createFromObject({element:'td', text:dayNumber, attributes:{className:'jsweld_calendar_thisMonth'}}));
						}
						else row.appendChild(jsweld.wcDOM.createFromObject({element:'td', attributes:{className:'jsweld_calendar_today'}, children:[{element:'a', text:dayNumber, attributes:{className:eventClass, href:'#', onclick:function(){thisRef.onDateSelect(new Date(year,month,this.firstChild.nodeValue)); return false;}}}]}));
						dayNumber++;
					}
					else 
					{
						if(!hasStarted)row.appendChild(jsweld.wcDOM.createFromObject({element:'td', text:prevMonthDayNumber++, attributes:{className:'jsweld_calendar_otherMonth'}}));
							else row.appendChild(jsweld.wcDOM.createFromObject({element:'td', text:nextMonthDayNumber++, attributes:{className:'jsweld_calendar_otherMonth'}}));
					}
				}
			}
			
			jsweld.wcDOM.clearNode(this.node);
			this.node.appendChild(jsweld.wcDOM.createFromObject({element:'table', children:[{node:calendarHead},{node:calendarBody}]}));							
		},
		
		/* 
			FUNCTION: onDateSelect
			DESCRIPTION: called when a date is selected. Subclasses of CALENDARBASE should override this function
							to allow for interactivity.
			ARGUMENTS: a_date - the date object representing the selected date [DATE]
			RETURNS: void
		*/
		onDateSelect: function(a_date){/* no default behavior */},
		
		/* 
			FUNCTION: willAllowPrevious
			DESCRIPTION: specifies whether or not the calendar will allow previous dates to be selectd.
							This function does not trigger an automatic redraw! displayDate will need
							to be called for the change to take effect.
							DEFAULT = TRUE.
			ARGUMENTS: a_boolean [BOOLEAN]
			RETURNS: void
		*/
		willAllowPrevious: function(a_boolean)
		{
			this.allowPrevious = a_boolean;
		},
		
		/* 
			FUNCTION: hasEventOnDay
			DESCRIPTION:
			ARGUMENTS: a_date - the date object representing the selected date [DATE]
			RETURNS: void
		*/
		hasEventOnDay: function(a_date)
		{
			return false;	
		}
	});
	
	/*------------------------------------------------------------------------------------
		JSWELD.CALENDAR.CALENDAREVENT - an event on the calendar
	--------------------------------------------------------------------------------------*/
	jsweld.calendar.calendarevent = Class.create(
	{
		/* 
			FUNCTION: constructor
			DESCRIPTION: 
			ARGUMENTS: a_url - location of the xml file on the server.
		*/
		initialize: function()
		{
			this.start = null;
			this.end = null;
			this.title = null;
			this.location = null;
			this.description = null;
			this.url = null;
			this.furtherUrl = null;
		},
		friendlyTime: function(a_date)
		{
			var isPM = (a_date.getHours()>12); 
			var hour = (isPM)?a_date.getHours()-12:a_date.getHours();
			var minute = (a_date.getMinutes()<10)?'0' + a_date.getMinutes():a_date.getMinutes();
			return hour + ':' + minute + ((isPM)?'pm':'am'); 
		},
		getStringStart: function() {return this.friendlyTime(this.start);},
		getStringEnd: function() {return this.friendlyTime(this.end);},
		
		/* 
			FUNCTION: getters and setters
			DESCRIPTION: all setters return a reference to the event allowing for chaining
		*/
		getStart: function(){return this.start;},
		setStart: function(a_date){this.start = a_date; return this;},
		
		getEnd: function(){return this.end;},
		setEnd: function(a_date){this.end = a_date; return this;},
		
		getTitle: function(){return this.title;},
		setTitle: function(a_string){this.title = a_string; return this;},
		
		getLocation: function(){return this.location;},
		setLocation: function(a_string){this.location = a_string; return this;},
		
		getDescription: function(){return this.description;},
		getBriefDescription: function()
		{
			var trimmedDescription = "";
			
			if(this.description !=null)
			{
				var wordPattern = /\s+/g;
				var wordCount = 10;
				var matches = this.description.split(wordPattern,wordCount);
					
				for(var i=0; i<matches.length; i++)
				{
					trimmedDescription += matches[i];
					if(i+1<matches.length)trimmedDescription += ' ';
				}
				trimmedDescription += '...';
			}
			
			return trimmedDescription
		},
		setDescription: function(a_string){this.description = a_string; return this;},
		
		getUrl: function(){return this.url;},
		setUrl: function(a_string){this.url = a_string; return this;},
		
		getFurtherUrl: function(){return this.furtherUrl;},
		setFurtherUrl: function(a_string){this.furtherUrl = a_string; return this;}
	});
	
	/*------------------------------------------------------------------------------------
		JSWELD.CALENDAR.CALENDARSCHEDULE - reads and parses an xml file for scheduled events.
	--------------------------------------------------------------------------------------*/
	jsweld.calendar.calendarschedule = Class.create(
	{
		/* 
			FUNCTION: constructor
			DESCRIPTION: 
			ARGUMENTS: a_url - location of the xml file on the server.
		*/
		initialize: function(a_url, a_callback)
		{
			this.url = a_url;
			this.finishedCallback = a_callback;
			this.comparator = function(a,b){return (a.getStart().getTime() - b.getStart().getTime());};
			this.events = new jsweld.collections.orderedlist(this.comparator);
			this.loaded = false;
		},
		loadEvents: function()
		{
			var iso8601Pattern = /([0-9]{4})-?([0-9]{2})-?([0-9]{2})(?:t|T)([0-9]{2}):?([0-9]{2})/;
			var eventsRef = this.events;
			var callbackRef = this.finishedCallback;
			var callback = function(a_response)
			{
				var calendarNode = a_response.firstChild;
				if(calendarNode.nodeName.toLowerCase()=="xml")calendarNode = a_response.childNodes[1];
				if(calendarNode.nodeName.toLowerCase()=="calendar")
				{
					for(var i=0; i<calendarNode.childNodes.length; i++)
					{
						var eventNode = calendarNode.childNodes[i];
						if(eventNode.nodeName.toLowerCase()=="event")
						{
							var currentEvent = new jsweld.calendar.calendarevent();
							
							for(var j=0; j<eventNode.childNodes.length; j++)
							{
								var currentNode = eventNode.childNodes[j];
								var currentNodeName = currentNode.nodeName.toLowerCase();
								
								if(currentNodeName=="start")
								{
									var startTime = currentNode.firstChild.nodeValue;
									var timePieces = iso8601Pattern.exec(startTime);
									if(timePieces!=null)
									{
										currentEvent.setStart(new Date(timePieces[1],timePieces[2]-1,timePieces[3],timePieces[4],timePieces[5]));	
									}
								}
								if(currentNodeName=="end")
								{
									var startTime = currentNode.firstChild.nodeValue;
									var timePieces = iso8601Pattern.exec(startTime);
									if(timePieces!=null)
									{
										currentEvent.setEnd(new Date(timePieces[1],timePieces[2]-1,timePieces[3],timePieces[4],timePieces[5]));	
									}
								}
								if(currentNodeName=="title")currentEvent.setTitle(currentNode.firstChild.nodeValue);
								if(currentNodeName=="url")currentEvent.setUrl(currentNode.firstChild.nodeValue);
								if(currentNodeName=="further-info")currentEvent.setFurtherUrl(currentNode.firstChild.nodeValue);
								if(currentNodeName=="location")
								{
									var tLocation = '';
									
									for(var a=0; a<currentNode.childNodes.length; a++)
									{
										var lineNode = currentNode.childNodes[a];
										if(lineNode.nodeName.toLowerCase()=='line')
										{
											tLocation = lineNode.firstChild.nodeValue;
											break;
										}
									}
									
									currentEvent.setLocation(tLocation);
								}
								if(currentNodeName=="description")
								{
									var tDescription = '';
									
									for(var a=0; a<currentNode.childNodes.length; a++)
									{
										var lineNode = currentNode.childNodes[a];
										if(lineNode.nodeName.toLowerCase()=='line')
										{
											tDescription += (lineNode.firstChild.nodeValue + ' ');
										}
									}
									
									currentEvent.setDescription(tDescription);
								}
							}
							
							eventsRef.addItem(currentEvent);
						}
					}
				}
				if(callbackRef)callbackRef();
				this.loaded = true;
			}
			
			var connection = new jsweld.ajax.xmlDownload(this.url, callback);
				connection.send();
		},
		hasEventOnDate: function(a_date)
		{
			var startEvent = new jsweld.calendar.calendarevent().setStart(new Date(a_date.getFullYear(),a_date.getMonth(),a_date.getDate(),0,0));
			var endEvent = new jsweld.calendar.calendarevent().setStart(new Date(a_date.getFullYear(),a_date.getMonth(),a_date.getDate(),23,59));
			return this.events.getRange(startEvent,endEvent).hasNext();
		},
		getEventsForDate: function(a_date)
		{
			var startEvent = new jsweld.calendar.calendarevent().setStart(new Date(a_date.getFullYear(),a_date.getMonth(),a_date.getDate(),0,0));
			var endEvent = new jsweld.calendar.calendarevent().setStart(new Date(a_date.getFullYear(),a_date.getMonth(),a_date.getDate(),23,59));
			return this.events.getRange(startEvent,endEvent);
		},
		getUpcomingEvents: function(a_max)
		{
			var startEvent = new jsweld.calendar.calendarevent().setStart(new Date());
			var upcEvents = this.events.getRangeNum(startEvent,a_max);
			return upcEvents;
		},
		isLoaded: function()
		{
			return this.loaded;	
		}
	});
	
	
	/*------------------------------------------------------------------------------------
	WELDMENU.KILLTIMER - A timer that initiates multiple callbacks on completion
	--------------------------------------------------------------------------------------*/	
	jsweld.killTimer = Class.create({
									  
		/* 
			FUNCTION: constructor
			DESCRIPTION: creates a kill timer with a particular duration.
			ARGUMENTS: a_duration:number - number of seconds to run
		*/
			initialize: function(a_duration)
			{
				this.duration = (a_duration>0)?a_duration*1000:1000;
				this.timer = null;
				this.callbacks = new Array();
			},
			
		/* 
			FUNCTION: startTimer
			DESCRIPTION: starts the timer and adds callback, this can be called multiple times when the timer is running.
			ARGUMENTS: a_calback:function - a no argument callback to be notified on timer completion.
			RETURNS: none
		*/
			startTimer: function(a_callback)
			{	
				if(!this.timer)
				{
					var thisRef = this;
					this.timer = window.setTimeout(function(){thisRef.notify();},this.duration);
				}
				this.callbacks.push(a_callback);	
			},
							
		/* 
			FUNCTION: stopTimer
			DESCRIPTION: stops and clears the timer, but does not notify the callbacks of completion
			ARGUMENTS: none
			RETURNS: none
		*/
			stopTimer: function()
			{
				this.callbacks = new Array();
				if(this.timer)
				{
					window.clearTimeout(this.timer);
					this.timer = null;
				}
			},
			
		/* 
			FUNCTION: notify
			DESCRIPTION: stops the timer and notifies callbacks!
			ARGUMENTS: none
			RETURNS: none
		*/
			notify: function()
			{
				for(var i=0; i< this.callbacks.length; i++)
				{
					this.callbacks[i]();
				}
				this.stopTimer();
			},
		
		/* 
			FUNCTION: isRunning
			DESCRIPTION: returns true if timer is running.
			ARGUMENTS: none
			RETURNS: boolean
		*/
			isRunning: function()
			{
				return (this.timer!=null);
			}
	});
	
	/*------------------------------------------------------------------------------------
		JSWELD.CALENDAR.SIMPLEEVENTCALENDAR - an event on the calendar
	--------------------------------------------------------------------------------------*/
	jsweld.calendar.simpleeventcalendar = Class.create(jsweld.calendar.basecalendar,
	{
		initialize: function($super,a_node,a_url)
		{
			
			var thisRef = this;
			var onEventsLoad = function(){$super(a_node);}
			this.events = new jsweld.calendar.calendarschedule(a_url, function(){onEventsLoad();});
			this.events.loadEvents();
			this.selectedEvent = null;
		},
		displayDate: function($super,a_month,a_year)
		{
			if(this.selectedEvent)
			{
				this.selectedEvent.kill();
				this.selectedEvent = null;
			}
			
			$super(a_month,a_year);
			
			var thisRef = this;
			var eventDays = $(this.node).select('.jsweld_calendar_event');
			for(var i=0; i<eventDays.length; i++)
			{
				var onMouseOver = function()
				{
					thisRef.onDateSelect(new Date(thisRef._year,thisRef._month,this.firstChild.nodeValue),this);
				}
				var onMouseOut = function()
				{
					if(thisRef.selectedEvent)thisRef.selectedEvent.closeIt();
				}
				eventDays[i].onmouseover = onMouseOver;	
				eventDays[i].onmouseout = onMouseOut;
			}
		},
		hasEventOnDay: function(a_date)
		{
			return (this.events)?this.events.hasEventOnDate(a_date):false;	
		},
		onDateSelect: function(a_date,a_node)
		{
			if(a_node)
			{
				var dateEvents = this.events.getEventsForDate(a_date);
				if(dateEvents.hasNext())
				{
					if(this.selectedEvent)this.selectedEvent.kill();
					
					var sEvent = new jsweld.calendar.slidingevent(a_node,dateEvents);
						sEvent.build();
						
					this.selectedEvent = sEvent;
				}
			}
		}
	});
	
	/*------------------------------------------------------------------------------------
		JSWELD.CALENDAR.ADVANCEDEVENTCALENDAR - an event on the calendar
	--------------------------------------------------------------------------------------*/
	jsweld.calendar.advancedeventcalendar = Class.create(jsweld.calendar.simpleeventcalendar,
	{
		initialize: function($super,a_node,a_url,a_upcomingNode,a_num)
		{
			this._numToDisplay = (a_num)?a_num:3;
			this._upcomingNode = a_upcomingNode;
			$super(a_node,a_url);
		},
		displayDate: function($super,a_month,a_year)
		{
			$super(a_month,a_year);
			this.drawUpcomingEvents();
		},
		drawUpcomingEvents: function()
		{
			jsweld.wcDOM.clearNode(this._upcomingNode);
			var upcoming = this.events.getUpcomingEvents(this._numToDisplay);
			
			while(upcoming.hasNext())
			{
				var currentEvent = upcoming.next();

				var dateText = jsweld.calendar.constants.MONTHS[currentEvent.getStart().getMonth()] + ' ' + currentEvent.getStart().getDate() + ', ' + currentEvent.getStart().getFullYear();
				this._upcomingNode.appendChild(jsweld.wcDOM.createFromObject({element:'h4', text:dateText}));

				
				this._upcomingNode.appendChild(jsweld.wcDOM.createFromObject({element:'table', children:
																	   [
																			{element:'thead', children:
																				[
																					{element:'tr', children:[{element:'th', text:currentEvent.getTitle(), attributes:{colSpan:2}}]}
																				]
																			},
																			{element:'tbody', children:
																				[
																					{element:'tr', children:[{element:'td', text:'when:', attributes:{className:'jsweld_calendar_advancedeventcalendar_upcoming_leftColumn'}},{element:'td', text:currentEvent.getStringStart() + ' - ' + currentEvent.getStringEnd()}]},
																					{element:'tr', children:[{element:'td', text:'where:', attributes:{className:'jsweld_calendar_advancedeventcalendar_upcoming_leftColumn'}},{element:'td', text:currentEvent.getLocation()}]},
																					{element:'tr', children:[{element:'td', text:'what:', attributes:{className:'jsweld_calendar_advancedeventcalendar_upcoming_leftColumn'}},{element:'td', text:currentEvent.getBriefDescription()}]},
																					{element:'tr', children:[{element:'td', attributes:{className:'jsweld_calendar_advancedeventcalendar_upcoming_leftColumn'}},{element:'td', children:[{element:'a', text:'> Full Details', attributes:{href:currentEvent.getFurtherUrl()}}]}]}
																				]
																			}
																	   ]
																	   }));
			}
		}
	});
	
	/*------------------------------------------------------------------------------------
		JSWELD.CALENDAR.SLIDINGEVENT
	--------------------------------------------------------------------------------------*/
	jsweld.calendar.slidingevent = Class.create(
	{
		initialize: function(a_parent,a_eventIterator)
		{
			this.node = null;
			this.parent = a_parent;
			this.events = a_eventIterator;
			this.timer = new jsweld.killTimer(1);
		},
		build: function()
		{
			var thisRef = this;
			var container = new Element('div',{'class':'jsweld_calendar_slidingevent_body'}).hide();
				container.onmouseover = function(){thisRef.reprieve();}
				container.onmouseout = function(){thisRef.closeIt();}
				
			var containerTop = new Element('div',{'class':'jsweld_calendar_slidingevent_top'});
			var containerMsg = new Element('div',{'class':'jsweld_calendar_slidingevent_msg'});
			var containerBottom = new Element('div',{'class':'jsweld_calendar_slidingevent_bottom'});
			
				//container.appendChild(containerTop);
				container.appendChild(containerMsg);
				//container.appendChild(containerBottom);
			
			var isFirst = true;
			while(this.events.hasNext())
			{
				var currentEvent = this.events.next();
				if(isFirst)
				{
					var dateText = jsweld.calendar.constants.MONTHS[currentEvent.getStart().getMonth()] + ' ' + currentEvent.getStart().getDate() + ', ' + currentEvent.getStart().getFullYear();
					containerMsg.appendChild(jsweld.wcDOM.createFromObject({element:'h4', text:dateText}));
					isFirst = false;	
				}
				
				containerMsg.appendChild(jsweld.wcDOM.createFromObject({element:'table', children:
																	   [
																			{element:'thead', children:
																				[
																					{element:'tr', children:[{element:'th', text:currentEvent.getTitle(), attributes:{colSpan:2}}]}
																				]
																			},
																			{element:'tbody', children:
																				[
																					{element:'tr', children:[{element:'td', text:'when:', attributes:{className:'jsweld_calendar_slidingevent_msg_leftColumn'}},{element:'td', text:currentEvent.getStringStart() + ' - ' + currentEvent.getStringEnd()}]},
																					{element:'tr', children:[{element:'td', text:'where:', attributes:{className:'jsweld_calendar_slidingevent_msg_leftColumn'}},{element:'td', text:currentEvent.getLocation()}]},
																					{element:'tr', children:[{element:'td', text:'what:', attributes:{className:'jsweld_calendar_slidingevent_msg_leftColumn'}},{element:'td', text:currentEvent.getBriefDescription()}]}
																				]
																			}
																	   ]
																	   }));
			}
			
			this.node = new Element('div');
			this.node.appendChild(container);
			document.body.appendChild(this.node);
			
			var containerHeight = container.getHeight();
			var containerWidth = container.getWidth();
			var parentPosition = this.parent.cumulativeOffset();
			var parentDimensions = this.parent.getDimensions();
			
			if(this.isDisplayRight(parentPosition.left))var x = parentPosition.left + parentDimensions.width + 'px';
				else var x = parentPosition.left - containerWidth + 'px';	
			var y = parentPosition.top + Math.floor(parentDimensions.height/2) - Math.floor(containerHeight/2) + 'px';
			
			this.node.setStyle({position:'absolute', left:x, top:y});
				
			container.show();
		},
		isDisplayRight: function(a_x)
		{
			return ((a_x-document.viewport.getScrollOffsets().left)<(document.viewport.getWidth()/2));
		},
		reprieve: function()
		{
			this.timer.stopTimer();	
		},
		closeIt: function()
		{
			var thisRef = this;
			if(!this.timer.isRunning())this.timer.startTimer(function(){thisRef.kill();});
		},
		kill: function()
		{
			if(this.node.parentNode)this.node.parentNode.removeChild(this.node);	
		}
	});
