// Constants
// Note: these are markers in the RSS feed's body field marking sub-fields for parsing
var FIELD_DATE			= "When:";
var FIELD_LOCATION		= "Where:";
var FIELD_STATUS		= "Event Status:";
var FIELD_DESCRIPTION	= "Event Description:"


// -------------------------------------------------------------------
// gAjax RSS Feeds Displayer- By Dynamic Drive, available at: http://www.dynamicdrive.com
// Created: July 17th, 2007 Updated: n/a
// -------------------------------------------------------------------

var gfeedfetcher_loading_image="indicator.gif" //Full URL to "loading" image. No need to config after this line!!

google.load("feeds", "1") //Load Google Ajax Feed API (version 1)

function gfeedfetcher(divid, divClass, linktarget){
	this.linktarget=linktarget || "" //link target of RSS entries
	this.feedlabels=[] //array holding lables for each RSS feed
	this.feedurls=[]
	this.feeds=[] //array holding combined RSS feeds' entries from Feed API (result.feed.entries)
	this.feedsfetched=0 //number of feeds fetched
	this.feedlimit=5
	this.showoptions="" //Optional components of RSS entry to show (none by default)
	this.sortstring="date" //sort by "date" by default
	document.write('<div id="'+divid+'" class="'+divClass+'"></div>') //output div to contain RSS entries
	this.feedcontainer=document.getElementById(divid)
	this.itemcontainer="<li>" //default element wrapping around each RSS entry item
}

gfeedfetcher.prototype.addFeed=function(label, url){
	this.feedlabels[this.feedlabels.length]=label
	this.feedurls[this.feedurls.length]=url
}

gfeedfetcher.prototype.filterfeed=function(feedlimit, sortstr){
	this.feedlimit=feedlimit
	if (typeof sortstr!="undefined")
	this.sortstring=sortstr
}

gfeedfetcher.prototype.displayoptions=function(parts){
	this.showoptions=parts //set RSS entry options to show ("date, datetime, time, snippet, label, description")
}

gfeedfetcher.prototype.setentrycontainer=function(containerstr){  //set element that should wrap around each RSS entry item
this.itemcontainer="<"+containerstr.toLowerCase()+">"
}

gfeedfetcher.prototype.init=function(){
	this.feedsfetched=0 //reset number of feeds fetched to 0 (in case init() is called more than once)
	this.feeds=[] //reset feeds[] array to empty (in case init() is called more than once)
	this.feedcontainer.innerHTML='<img src="'+gfeedfetcher_loading_image+'" /> Retrieving RSS feed(s)'
	var displayer=this
	for (var i=0; i<this.feedurls.length; i++){ //loop through the specified RSS feeds' URLs
		var feedpointer=new google.feeds.Feed(this.feedurls[i]) //create new instance of Google Ajax Feed API
		var items_to_show=(this.feedlimit<=this.feedurls.length)? 1 : Math.floor(this.feedlimit/this.feedurls.length) //Calculate # of entries to show for each RSS feed
		if (this.feedlimit%this.feedurls.length>0 && this.feedlimit>this.feedurls.length && i==this.feedurls.length-1) //If this is the last RSS feed, and feedlimit/feedurls.length yields a remainder
			items_to_show+=(this.feedlimit%this.feedurls.length) //Add that remainder to the number of entries to show for last RSS feed
		feedpointer.setNumEntries(items_to_show) //set number of items to display
		feedpointer.load(function(r){displayer._fetch_data_as_array(r)}) //call Feed.load() to retrieve and output RSS feed
	}
}

gfeedfetcher._formatdate=function(datestr, showoptions){
	var itemdate=new Date(datestr)
	var parseddate=(showoptions.indexOf("datetime")!=-1)? itemdate.toLocaleString() : (showoptions.indexOf("date")!=-1)? itemdate.toLocaleDateString() : (showoptions.indexOf("time")!=-1)? itemdate.toLocaleTimeString() : ""
	return "<span class='datefield'>"+parseddate+"</span>"
}

gfeedfetcher._sortarray=function(arr, sortstr){
	var sortstr=(sortstr=="label")? "ddlabel" : sortstr //change "label" string (if entered) to "ddlabel" instead, for internal use
	if (sortstr=="title" || sortstr=="ddlabel"){ //sort array by "title" or "ddlabel" property of RSS feed entries[]
		arr.sort(function(b,a){
		var fielda=a[sortstr].toLowerCase()
		var fieldb=b[sortstr].toLowerCase()
		return (fielda<fieldb)? -1 : (fielda>fieldb)? 1 : 0
		})
	}
	else{ //else, sort by "publishedDate" property (using error handling, as "publishedDate" may not be a valid date str if an error has occured while getting feed
		try{
			arr.sort(function(b,a){return new Date(b.parsedValues.date)-new Date(a.parsedValues.date)})
		}
		catch(err){}
	}
}

gfeedfetcher.prototype._fetch_data_as_array=function(result){
	var thisfeed=(!result.error)? result.feed.entries : "" //get all feed entries as a JSON array or "" if failed
	if (thisfeed=="") //if error has occured fetching feed
		alert("Google Feed API Error: "+result.error.message)
	for (var i=0; i<thisfeed.length; i++) //For each entry within feed
		result.feed.entries[i].ddlabel=this.feedlabels[this.feedsfetched] //extend it with a "ddlabel" property
	this.feeds=this.feeds.concat(thisfeed) //add entry to array holding all feed entries
	this._signaldownloadcomplete() //signal the retrieval of this feed as complete (and move on to next one if defined)
}

gfeedfetcher.prototype._signaldownloadcomplete=function(){
	this.feedsfetched+=1
	if (this.feedsfetched==this.feedurls.length) //if all feeds fetched
		this._displayresult(this.feeds) //display results
}


gfeedfetcher.prototype._displayresult=function(feeds){
	var rssoutput=(this.itemcontainer=="<li>")? "<ul>\n" : ""
	
	for (var i=0; i<feeds.length; i++)
	{
		ParseEventDescription(feeds[i]);
		
		// Swap event date for publish date
//		if (feeds[i].parsedValues.date)
			feeds[i].publishedDate = feeds[i].parsedValues.dateString;
	}
	
	gfeedfetcher._sortarray(feeds, this.sortstring)
	
	for (var i=0; i<feeds.length; i++)
	{
		var oToday = new Date();
		
		if (feeds[i].parsedValues.date >= oToday)
		{
			var moreinfolink='<a class="moreinfo" href="' + feeds[i].link + '" target="_blank">more details...</a>';
			
			if (feeds[i].link == "")
				moreinfolink="";
			
			var itemtitle="<a href=\"" + feeds[i].link + "\" target=\"" + this.linktarget + "\" class=\"titlefield\">" + feeds[i].title + "</a>"
			var itemlabel=/label/i.test(this.showoptions)? '<span class="labelfield">['+this.feeds[i].ddlabel+']</span>' : " "
			
			var itemdate = "<span class='datefield'>"+feeds[i].parsedValues.dateString+"</span>";
			
//			if (feeds[i].parsedValues.includesTime == true)
//				itemdate = gfeedfetcher._formatdate(feeds[i].publishedDate, this.showoptions);
//			
//			else
//				itemdate = gfeedfetcher._formatdate(feeds[i].publishedDate, this.showoptions.replace("datetime", "date"));
			
			var content = "";
			
			// When
			if (feeds[i].parsedValues.dateString != "")
				content += '<span class="whenfield"><strong>' + FIELD_DATE + "</strong> " + feeds[i].parsedValues.dateString + "</span>";
			
			// Where
			if (feeds[i].parsedValues.location != "")
				content += '<span class="wherefield"><strong>' + FIELD_LOCATION + "</strong> " + feeds[i].parsedValues.location + "</span>";
			
			// Description
			if (feeds[i].parsedValues.description != "")
				content += '<span class="descriptionfield"><strong>' + FIELD_DESCRIPTION + "</strong> " + feeds[i].parsedValues.description + "</span>";
			
			var itemdescription=/description/i.test(this.showoptions)? "<div class=\"bodyfield hidden\">"+content+moreinfolink+"</div>" : /snippet/i.test(this.showoptions)? "<div class=\"bodyfield\">"+feeds[i].contentSnippet+moreinfolink+"</div>"  : ""
			
			rssoutput+=this.itemcontainer + itemtitle + " " + itemlabel + " " + itemdate + "\n" + itemdescription + this.itemcontainer.replace("<", "</") + "\n\n"
		}
	}
	
	rssoutput+=(this.itemcontainer=="<li>")? "</ul>" : ""
	
	this.feedcontainer.innerHTML=rssoutput
	
	// Add expand/collapse
	this.feedcontainer.onclick=rsscalendar_onclick;
}

function ParseEventDescription(oFeed)
// Reads the description field of the RSS feed entry, and pulls out known
// data points like location, status, date, etc. and adds them to the entry object.
{
	var sEventDescription = oFeed.content + "<br />";
	
	var iDateIndex			= sEventDescription.indexOf(FIELD_DATE);
	var iLocationIndex		= sEventDescription.indexOf(FIELD_LOCATION);
	var iStatusIndex		= sEventDescription.indexOf(FIELD_STATUS);
	var iDescriptionIndex	= sEventDescription.indexOf(FIELD_DESCRIPTION);
	
	oFeed.parsedValues = new Object();
	
	oFeed.parsedValues.date			= "";
	oFeed.parsedValues.location		= "";
	oFeed.parsedValues.status		= "";
	oFeed.parsedValues.description	= "";
	
	// Date
	if (iDateIndex != -1)
	{
		var iEndOfFieldIndex	= sEventDescription.indexOf("<br", iDateIndex);
		var sFieldData			= sEventDescription.substring(iDateIndex + FIELD_DATE.length, iEndOfFieldIndex);
		
		oFeed.parsedValues.dateString	= Trim(sFieldData);
		oFeed.parsedValues.date			= ParseDate(Trim(sFieldData));
		
		// Includes time
		if (sFieldData.toLowerCase().indexOf(":") != -1)
			oFeed.parsedValues.includesTime = true;
		
		else
			oFeed.parsedValues.includesTime = false;
	}
	
	// Location
	if (iLocationIndex != -1)
	{
		var iEndOfFieldIndex	= sEventDescription.indexOf("<br", iLocationIndex);
		var sFieldData			= sEventDescription.substring(iLocationIndex + FIELD_LOCATION.length, iEndOfFieldIndex);
		
		oFeed.parsedValues.location = Trim(sFieldData);
	}
	
	// Status
	if (iStatusIndex != -1)
	{
		var iEndOfFieldIndex	= sEventDescription.indexOf("<br", iStatusIndex);
		var sFieldData			= sEventDescription.substring(iStatusIndex + FIELD_STATUS.length, iEndOfFieldIndex);
		
		oFeed.parsedValues.status = Trim(sFieldData);
	}
	
	// Description
	if (iDescriptionIndex != -1)
	{
		var iEndOfFieldIndex	= sEventDescription.indexOf("<br", iDescriptionIndex);
		var sFieldData			= sEventDescription.substring(iDescriptionIndex + FIELD_DESCRIPTION.length, iEndOfFieldIndex);
		
		oFeed.parsedValues.description = Trim(sFieldData);
	}
}

function rsscalendar_onclick(oEvent)
// Check to see if the title was clicked, if so, expand/collapse the body.
{
	oEvent = ExtendedEvent(oEvent);
	
	var bFollowLink = true;
	
	// Get the clicked element
	var eElement = ExtendedElement(oEvent.source);
	
	// Title elements
	if (eElement.isOfClass("titlefield"))
	{
		bFollowLink = false;
		
		var eTitle		= eElement;
		var eEntry		= ExtendedElement(eElement.parentNode);
		var eEntries	= ExtendedElement(eEntry.findNearestParentOfClass("RSSCalendar"));
		var eBody		= ExtendedElement(eEntry.firstChildOfClass("bodyfield"));
		
		// Collapse all others first
		var aEntries = eEntries.getElementsByTagName("li");
		
		for (var i = 0; i < aEntries.length; i++)
		{
			var eThisEntry = aEntries[i];
			
			// Don't collapse the current entry
			if (eThisEntry != eEntry)
				CollapseEntry(eThisEntry);
		}
		
		// Show body
		if (eBody.isOfClass("hidden"))
		{
			ExpandEntry(eEntry);
		}
		
		// Hide body
		else
		{
			CollapseEntry(eEntry);
		}
	}
	
	// More info link
	else if (eElement.isOfClass("moreinfo"))
	{
		bFollowLink = false;
		
		var sURL			= eElement.href;
		var sWindowName		= "";
		var iWidth			= 640;
		var iHeight			= 400;
		var bFixedSize		= false;
		var bMenuBar		= false;
		var bLocationBar	= true;
		
		NewWindow(sURL, sWindowName, iWidth, iHeight, bFixedSize, bMenuBar, bLocationBar)
	}
	
	// Cancel any hyperlinks
	return bFollowLink;
}

function CollapseEntry(eEntry)
{
	eEntry = ExtendedElement(eEntry);
	
	// Get the body element
	var eBody = ExtendedElement(eEntry.firstChildOfClass("bodyfield"));
	
	eBody.addStyleClass("hidden");
	eEntry.removeStyleClass("expanded");
}

function ExpandEntry(eEntry)
{
	eEntry = ExtendedElement(eEntry);
	
	// Get the body element
	var eBody = ExtendedElement(eEntry.firstChildOfClass("bodyfield"));
	
	eBody.removeStyleClass("hidden");
	eEntry.addStyleClass("expanded");
}

function ParseDate(sDateString)
{
	// Strip timezone
//	if (sDateString.indexOf(":") != -1)
//		sDateString = sDateString.substring(0, sDateString.length - 3);
	
	// Remember to convert to 24hr time
	var bConvertTo24Hour = false;
	
	if ((sDateString.toLowerCase().indexOf("am") != -1)
		|| (sDateString.toLowerCase().indexOf("pm") != -1))
		bConvertTo24Hour = true;
	
	var iNumberOfCharacters	= sDateString.length;
	var oDate				= null;
	
	for (var i = 0; i < iNumberOfCharacters; i++)
	{
		try
		{
			// Parse
			oDate = Date.parse(sDateString);
			
			// Stop trying
			if (!isNaN(oDate))
				i = iNumberOfCharacters;
			
			else
			{
				// Remove the last character
				if ((sDateString.length - 1) > 0)
					sDateString = sDateString.substring(0, sDateString.length - 1);
			}
		}
		
		catch(e)
		{
			// Remove the last character
			if ((sDateString.length - 1) > 0)
				sDateString = sDateString.substring(0, sDateString.length - 1);
			
			// Stop looking
			else
				i = iNumberOfCharacters;
		}
	}
	
	// Convert # of milliseconds to a Date object
	if (oDate)
	{
		var iNumberOfMilliseconds = oDate;
		
		oDate = new Date();
		oDate.setTime(iNumberOfMilliseconds);
	}
	
	if (oDate)
		if (bConvertTo24Hour)
		{
			oDate.setHours((oDate.getHours() + 12), oDate.getMinutes(), oDate.getSeconds(), 00);
		}
	
	return oDate;
}

function ExtendedEvent(oEvent)
// Returns a cross-browser version of the event object (for event handlers)
//
// Parameters:
//
// oEvent:		the event object itself or null
//
// Returns:		an ExtendedEvent object (wrapper)
{
	var oEventWrapper	= null;
	
	// Populate the event object
	if (oEvent)
		oEventWrapper	= oEvent;

	else if (typeof event != "undefined")
		oEventWrapper	= event;
	
	if (oEventWrapper)
	{
		// Normalize the related elements
		oEventWrapper.source	= (oEventWrapper.srcElement)	? oEventWrapper.srcElement	: oEventWrapper.target;
	}
	
	return oEventWrapper;
}

function ExtendedElement(eElement)
// Returns an extended element object
//
// Parameters:
//
// eElement:	an element
//
// Returns:		null
{
	var oExtendedElement = eElement;
	
	// Grab the element provided, or look it up from its ID
	if (typeof eElement == "string")
		oExtendedElement = document.getElementById(eElement);
	
	// Don't extend plugins
	if (oExtendedElement)
	{
		if ((oExtendedElement.tagName == "EMBED") || (oExtendedElement.tagName == "OBJECT"))
			oExtendedElement = null;
	}
	
	// If the object is valid
	if (oExtendedElement) // If the object is valid, then extend it
	{
		if (oExtendedElement._isExtended != true)
		{
			oExtendedElement._isExtended = true;
			
			// EXTENDED MEMBER FUNCTIONS
			oExtendedElement.isOfClass						= public_ExtendedElement_IsOfClass;
			oExtendedElement.firstChildOfClass				= public_ExtendedElement_FirstChildOfClass;
			oExtendedElement.setStyleClasses				= public_ExtendedElement_SetStyleClasses;
			oExtendedElement.getStyleClasses				= public_ExtendedElement_GetStyleClasses;
			oExtendedElement.addStyleClass					= public_ExtendedElement_AddStyleClass;
			oExtendedElement.removeStyleClass				= public_ExtendedElement_RemoveStyleClass;
			oExtendedElement.findNearestParentOfType		= public_ExtendedElement_FindNearestParentOfType;
			oExtendedElement.findNearestParentOfClass		= public_ExtendedElement_FindNearestParentOfClass;
			oExtendedElement.findNearestParentOfClassUntil	= public_ExtendedElement_FindNearestParentOfClassUntil;
			oExtendedElement.childrenOfClass				= public_ExtendedElement_ChildrenOfClass;
		}
	}
	
	return oExtendedElement;
}

function public_ExtendedElement_FirstChildOfClass(sClassName)
// Returns the first child with the specified class name.  Only 1st level children are searched.
//
// Parameters:
//
// sClassName:	The class name being searched for
//
// Returns:		An element or null
{
	var eReturnValue		= null;
	var i					= 0;
	var aChildren			= this.getElementsByTagName("*");
	var iNumberOfChildren	= aChildren.length;
	
	while ((i < iNumberOfChildren) && (!eReturnValue))
	{
		if (ExtendedElement(aChildren[i]).isOfClass(sClassName))
			eReturnValue = aChildren[i];
		
		i++;
	}
	
	return eReturnValue;
}

function public_ExtendedElement_ChildrenOfClass(sClassName)
// Returns an Array of children (not just 1st level children) who have
// sClassName specified.
//
// Parameters:
//
// sClassName:	a class name
//
// Returns:		an Array
{
	var aMatchingChildren	= new Array();
	var aChildren			= this.getElementsByTagName("*");
	var iNumberOfChildren	= aChildren.length;
	var i					= 0;
	
	while (i < iNumberOfChildren)
	{
		var eChild = aChildren[i];
		
		if (ExtendedElement(eChild).isOfClass(sClassName))
			aMatchingChildren[aMatchingChildren.length] = eChild;
		
		i++;
	}
	
	return aMatchingChildren;
}

function public_ExtendedElement_IsOfClass(sClassName)
// Returns whether or not the element has sClassName as one of its classes
//
// Parameters:
//
// sClassName:	a class name
//
// Returns:		boolean
{
	var bValue		= false;
	var bMatchFound	= false;
	var sClasses	= "";

	if (this.className != null)
		sClasses = this.className;

	var aClasses	= sClasses.split(" ");
	
	sClassName = sClassName.toLowerCase();
	
	var i = 0;
	
	while ((i < aClasses.length) && (!bMatchFound))
	{
		if (aClasses[i].toLowerCase() == sClassName)
		{
			bValue		= true;
			bMatchFound	= true;
		}
		
		i++;
	}
	
	return bValue;
}

function public_ExtendedElement_SetStyleClasses(sClassNames)
// Sets the class (CSS) names for the element
//
// Parameters:
//
// sClassNames:	the class names separated by spaces (ex.  "box square blue")
//
// Returns:		none
{
	this.className = sClassNames;
}

function public_ExtendedElement_GetStyleClasses()
// Returns the class (CSS) names for the element
//
// Parameters:	none
//
// Returns:		none
{
	var sReturnValue = "";
	
	if (this.className)
		sReturnValue = Trim(this.className);
		
	return sReturnValue;
}

function public_ExtendedElement_AddStyleClass(sClassName) 
// Adds style class to the existing set; ex. adds "box" to "square blue"
//
// Parameters:
//
// sClassName:	the class name
//
// Returns:		none
{
	sClassName = Trim(sClassName);
	
	if (!this.isOfClass(sClassName))
	{
		var sOldClasses	= Trim(this.getStyleClasses());
		var sSpacer		= "";
		
		if (sOldClasses.length > 0)
			sSpacer = " ";

		this.setStyleClasses(sOldClasses + sSpacer + sClassName);
	}
}

function public_ExtendedElement_RemoveStyleClass(sClassName) 
// Removes a style class from the existing set
//
// Parameters:
//
// sClassName:	the class name (1 only) to be removed; multiple occurances will be removed
//
// Returns:		none
{
	sClassName = Trim(sClassName);
	
	var re			= new RegExp("\\b" + sClassName + "\\b", "mgi");
	var sOldClasses	= this.getStyleClasses();
	var sNewClasses	= sOldClasses.replace(re, "");
	
	this.setStyleClasses(sNewClasses);
}

function public_ExtendedElement_FindNearestParentOfType(sTag)
// Looks upwards through the DOM to find the nearest parent of type sTag (ex. "TD" or "TABLE", etc.)
// and stops searching when it hits the HTML element.
//
// Parameters:
//
// sTag:		The tag we are looking for [all uppercase letters]
//
// Returns:		The found parent element (which may be HTML if nothing was found).
{
	var eParent = this;
	var bContinue = true;
	
	sTag = sTag.toUpperCase();

	while (bContinue)
	{
		if (eParent)
		{
			if (eParent.tagName.toUpperCase() == sTag)
				bContinue = false;
			
			if (eParent.tagName == "BODY")
			{
				bContinue	= false;
				eParent		= null;
			}
		}
		else // eParent == null
			bContinue = false;
		
		if (bContinue)
			eParent = eParent.parentNode;
	}

	return eParent; 
}

function public_ExtendedElement_FindNearestParentOfClass(sClassName)
// Looks upwards through the DOM to find the nearest parent with the class of sClassName
// or null if one is not found - stops searching at the BODY element
//
// Parameters:
//
// sClassName:		The class name we are looking for [case insensitive]
//
// Returns:			The found parent element (which may be null if nothing was found).
{
	var eBody = document.body;
	
	return this.findNearestParentOfClassUntil(sClassName, eBody);
}

function public_ExtendedElement_FindNearestParentOfClassUntil(sClassName, eStopElement)
// Looks upwards through the DOM to find the nearest parent with the class of sClassName
// or null if one is not found - stops searching at eStopElement
//
// Parameters:
//
// sClassName:		The class name we are looking for [case insensitive]
// eStopElement:	The last element to search
//
// Returns:			The found parent element (which may be null if nothing was found).
{
	var eParent = this;
	var bContinue = true;

	while (bContinue)
	{
		if (eParent)
		{
			if (eParent.isOfClass(sClassName))
				bContinue = false;
			
			if (eParent == eStopElement)
			{
				bContinue	= false;
				eParent		= null;
			}
		}
		
		else // eParent == null
			bContinue = false;

		if (bContinue)
			eParent = ExtendedElement(eParent.parentNode);
	}

	return eParent; 
}

function Trim(sString)
// Strips white space from a string
//
// Parameters:
//
// sString:		the string to strip
//
// Returns:		the string
{
	sString = sString.replace(/	/gim, "");
	sString = sString.replace(/&nbsp;/gim, "");
	sString = sString.replace(/[\f\n\r\t\v]/gim, "");
	
	// Is the left/right trim below redundant?

	return LeftTrim(RightTrim(sString));
}

function LeftTrim(sString)
// Strips white space from a string
//
// Parameters:
//
// sString:		the string to strip
//
// Returns:		the string
{
	var rgExp = /^\s */;

	return sString.replace(rgExp, "");
}

function RightTrim(sString)
// Strips white space from a string
//
// Parameters:
//
// sString:		the string to strip
//
// Returns:		the string
{
	var rgExp = /\s *$/;

	return sString.replace(rgExp, "");
}

function NewWindow(sURL, sWindowName, iWidth, iHeight, /* Optional --> */ bFixedSize, bMenuBar, bLocationBar)
// Opens and returns a new browser window
//
// Parameters:
//
// sURL:			location
// sWindowName:		name of the new window
// iWidth:			new width
// iHeight:			new height
// bFixedSize:		[optional] true if there should be no resize handles
// bMenuBar:		[optional] true if the menubar should be shown
// bLocationBar:	[optional] true if the URL line should be shown
//
// Returns:		a Window reference
{
	// Defaults
	var WINDOWS_TASK_BAR_HEIGHT	= 25; // px
	var iWidth			= iWidth || 640;
	var iHeight			= iHeight || 480;
	var sWindowName		= sWindowName || ("window_" + (new Date().getTime()));
	var sResizable		= "yes";
	var sMenuBar		= "no";
	var sLocationBar	= "no";
	
	if (bFixedSize)
		sResizable = "no";
	
	if (bMenuBar)
		sMenuBar = "yes";
	
	if (bLocationBar)
		sLocationBar = "yes";
	
	// Calculate the position of the window so that it is centered on screen
	var iScreenHeight	= window.screen.availHeight;
	var iScreenWidth	= window.screen.availWidth;
	var iLeft			= (iScreenWidth/2) - (iWidth/2);
	var iTop			= (iScreenHeight/2) - (iHeight/2) - WINDOWS_TASK_BAR_HEIGHT;
	
	// Describe the new window
	var sAttributes =
		",resizable=" + sResizable +
		",scrollbars=yes" +
		",menubar=" + sMenuBar +
		",location=" + sLocationBar +
		",status=yes" +
		",height=" + iHeight +
		",width=" + iWidth +
		",top=" + iTop +
		",left=" + iLeft;
	
	return window.open(sURL, sWindowName, sAttributes);
}

/*
	Date Format 1.1
	(c) 2007 Steven Levithan <stevenlevithan.com>
	MIT license
	With code by Scott Trenda (Z and o flags, and enhanced brevity)
*/

/*** dateFormat
	Accepts a date, a mask, or a date and a mask.
	Returns a formatted version of the given date.
	The date defaults to the current date/time.
	The mask defaults ``"ddd mmm d yyyy HH:MM:ss"``.
*/
var dateFormat = function () {
	var	token        = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloZ]|"[^"]*"|'[^']*'/g,
		timezone     = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
		timezoneClip = /[^-+\dA-Z]/g,
		pad = function (value, length) {
			value = String(value);
			length = parseInt(length) || 2;
			while (value.length < length)
				value = "0" + value;
			return value;
		};

	// Regexes and supporting functions are cached through closure
	return function (date, mask) {
		// Treat the first argument as a mask if it doesn't contain any numbers
		if (
			arguments.length == 1 &&
			(typeof date == "string" || date instanceof String) &&
			!/\d/.test(date)
		) {
			mask = date;
			date = undefined;
		}

		date = date ? new Date(date) : new Date();
		if (isNaN(date))
			throw "invalid date";

		var dF = dateFormat;
		mask   = String(dF.masks[mask] || mask || dF.masks["default"]);

		var	d = date.getDate(),
			D = date.getDay(),
			m = date.getMonth(),
			y = date.getFullYear(),
			H = date.getHours(),
			M = date.getMinutes(),
			s = date.getSeconds(),
			L = date.getMilliseconds(),
			o = date.getTimezoneOffset(),
			flags = {
				d:    d,
				dd:   pad(d),
				ddd:  dF.i18n.dayNames[D],
				dddd: dF.i18n.dayNames[D + 7],
				m:    m + 1,
				mm:   pad(m + 1),
				mmm:  dF.i18n.monthNames[m],
				mmmm: dF.i18n.monthNames[m + 12],
				yy:   String(y).slice(2),
				yyyy: y,
				h:    H % 12 || 12,
				hh:   pad(H % 12 || 12),
				H:    H,
				HH:   pad(H),
				M:    M,
				MM:   pad(M),
				s:    s,
				ss:   pad(s),
				l:    pad(L, 3),
				L:    pad(L > 99 ? Math.round(L / 10) : L),
				t:    H < 12 ? "a"  : "p",
				tt:   H < 12 ? "am" : "pm",
				T:    H < 12 ? "A"  : "P",
				TT:   H < 12 ? "AM" : "PM",
				Z:    (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
				o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4)
			};

		return mask.replace(token, function ($0) {
			return ($0 in flags) ? flags[$0] : $0.slice(1, $0.length - 1);
		});
	};
}();

// Some common format strings
dateFormat.masks = {
	"default":       "ddd mmm d yyyy HH:MM:ss",
	shortDate:       "m/d/yy",
	mediumDate:      "mmm d, yyyy",
	longDate:        "mmmm d, yyyy",
	fullDate:        "dddd, mmmm d, yyyy",
	shortTime:       "h:MM TT",
	mediumTime:      "h:MM:ss TT",
	longTime:        "h:MM:ss TT Z",
	isoDate:         "yyyy-mm-dd",
	isoTime:         "HH:MM:ss",
	isoDateTime:     "yyyy-mm-dd'T'HH:MM:ss",
	isoFullDateTime: "yyyy-mm-dd'T'HH:MM:ss.lo"
};

// Internationalization strings
dateFormat.i18n = {
	dayNames: [
		"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat",
		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
	],
	monthNames: [
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
	]
};

// For convenience...
Date.prototype.format = function (mask) {
	return dateFormat(this, mask);
}
