/******************************************************************************
 * University of Wisconsin Platteville
 *
 * University Centers Network Support - ResNet
 *
 * common.js
 *
 * This file contains common objects and functions that are meant
 * to be reused often.
 *
 * Contains Objects:
 *  Vector
 *  VectorMap
 *
 * Contains Functions:
 *  clone()
 *  sortNumeric()
 *  isArray()
 *  isObject()
 *  isFunction()
 *  isBoolean()
 *  isEmpty()
 *  isNull()
 *  isNumber()
 *  isInteger()
 *  isEven()
 *  getQueryVariable(name)
 *  searchArray(array, value)
 *  inArray(array, value)
 *
 * @author Adam Nowicki
 * @copyright 2006 ResNet Development
 */

/**
 * Adds the ability to clone to all objects
 *
 * A clone of an object is a duplicate of the original in a separate memory
 * space.  This is useful if you need to make a temporary object of the
 * original in which you make discardable changes to and want to retain the
 * original. 
 *
 * @param boolean Clone the object that are children of this
 * @return object New clone of the original object
 * @author Adam Nowicki
 */
Object.prototype.clone = function (deep)
{
	if ( !isEmpty(deep) && !isBoolean(deep) )
		throw "Exception: ";
	
	// Create a new object of the parent's type
	
	var objectClone = new this.constructor();
	
	// Set property to the next property in the object
	for ( var property in this )
		if (!deep)
			objectClone[property] = this[property];
		else if (typeof this[property] == 'object')
			objectClone[property] = this[property].clone(deep);
		else
			objectClone[property] = this[property];
	
	return objectClone;
}

/**
 * Adds the sortNumeric method for all array objects
 *
 * Takes the array and sorts it by comparing the numeric value of each of the
 * elements rather than comparing character by character.
 *
 * @param bool True sort the array from highest to lowest numeric value
 * @author Adam Nowicki
 */
Array.prototype.sortNumeric = function(highToLow)
{
	var compareNumeric = function(a, b)
	{
		if ( isEmpty(highToLow) || highToLow == false )
			return a - b;
		else
			return b - a;
	}
	
	this.sort(compareNumeric);
}

/**
 * Sorts the array by comparing each character and then reversing the result.
 *
 * @author Adam Nowicki
 */
Array.prototype.sortReverse = function()
{
	this.sort();
	this.reverse();
}

/**
 * Takes a string and returns its trimmed version.
 *
 * This removes all white spaces before the first printable character and all
 * the white spaces after the last printable character.
 *
 * @return string
 * @author Adam Nowicki
 */
function trimString(str)
{
	str = str.replace( /^\s+/g, "" );// strip leading
	return str.replace( /\s+$/g, "" );// strip trailing
}

/**
 * Takes the current string object and returns a trimmed version of it.
 *
 * @return string Trimmed version of the original
 * @author Adam Nowicki
 */
String.prototype.trim = function()
{
	return trimString(this);
}

/**
 * Takes the current string object's value and returns the result from the
 * toBoolean() function.
 *
 * @return boolean
 * @author Adam Nowicki
 */
String.prototype.toBoolean = function()
{
	return toBoolean(this);
}

/**
 * It will return true if the string in lowercase format is one of the following
 * values: true, t, yes, or y ; otherwise it will return false.
 *
 * @return boolean
 * @author Adam Nowicki
 */
function toBoolean(val)
{
	if ( val.toLowerCase() == "true" || val.toLowerCase() == "t" || val == true
		|| val.toLowerCase() == "yes" || val.toLowerCase() == "y" )
		return true;
	else
		return false;
}
/**
 * Checks to see if parameter is an array
 *
 * @param mixed
 * @return boolean
 * @author Adam Nowicki
 */
function isArray(val)
{
    return isObject(val) && val.constructor == Array;
}

/**
 * Checks to see if the parameter is an object
 *
 * @param mixed
 * @return boolean
 * @author Adam Nowicki
 */
function isObject(val)
{
    return (val && typeof val == 'object') || isFunction(val);
}

/**
 * Checks to see if the parameter is a function
 *
 * @param mixed
 * @return boolean
 * @author Adam Nowicki
 */
function isFunction(val)
{
    return typeof val == 'function';
}

/**
 * Checks to see if the parameter is boolean
 *
 * @param mixed
 * @return boolean
 * @author Adam Nowicki
 */
function isBoolean(val)
{
    return typeof val == 'boolean';
}

/**
 * Checks to see if the parameter is empty
 *
 * @param mixed
 * @return boolean
 * @author Adam Nowicki
 */
function isEmpty(obj)
{
	var i, v;
	
	if ( isObject(obj) )
	{
		for (i in obj)
		{
			v = obj[i];
			if ( isUndefined(v) && isFunction(v) )
				return false;
		}
	}
	return true;
}

function isUndefined(obj)
{
	return obj == undefined;
}

/**
 * Checks to see if the parameter is null.  This does not evaluate true for
 * uninitiallize variables. (Use isEmpty in that case).
 *
 * @param mixed
 * @return boolean
 * @author Adam Nowicki
 */
function isNull(val)
{
    return (typeof val == 'object' && !val) || isUndefined(val);
}

/**
 * Checks to see if the parameter is a number
 *
 * @param mixed
 * @return boolean
 * @author Adam Nowicki
 */
function isNumber(val)
{
    return typeof val == 'number' && isFinite(val);
}

/**
 * Checks to see if the parameter is a string
 *
 * @param string
 * @return boolean
 * @author Adam Nowicki
 */
function isString(val)
{
	return typeof val == 'string' && !val;
}

/**
 * Checks to see if the parameter is an integer
 *
 * @param mixed
 * @return boolean
 * @author Adam Nowicki
 */
function isInteger(val)
{
	if ( isNumber(val) && val == Math.round(val) )
		return true;
	else
		return false;
}

/**
 * Checks to see if the parameter is an even number
 *
 * @param integer
 * @return boolean
 * @author Adam Nowicki
 */
function isEven(num)
{
	if ( !isInteger(num) )
		throw "Exception: isEven was passed a non-integer";
	else if ( num % 2 == 0 )
		return true;
	else
		return false;
}

/**
 * Checks to see if the current browswer is Internet Explorer (or ActiveX
 * capable).
 *
 * @return boolean
 * @author Adam Nowicki
 */
function isIE()
{
	if ( window.ActiveXObject )
		return true;
	else
		return false;
}

/**
 * Returns a new string with the value of the current one passed through
 * the firstUpper() function.
 *
 * @return string
 * @author Adam Nowicki
 */
String.prototype.firstUpper = function()
{
	return firstUpper( this );
}

/**
 * Takes a string and capitalizes the first character.
 *
 * @return string
 * @author Adam Nowicki
 */
function firstUpper( str )
{
	return str.substring(0,1).toUpperCase() + str.substring(1, str.length);
}

/**
 * Returns a string of the default XML header.
 *
 * @return string
 * @author Adam Nowicki
 */
function getXMLHeader()
{
	return '<?xml version="1.0" encoding="ISO-8859-1"?>';
}

function createDocument()
{
	var xmldoc;
	
	if ( window.ActiveXObject )
	{
		xmldoc = new ActiveXObject("Microsoft.XMLDOM");
	}
	else if ( document.implementation.createDocument )
	{
		xmldoc = document.implementation.createDocument("","",null);
	}
	else
	{
		alert("Your browser doesn't support XML DOM");
		return;
	}
	return xmldoc;
}

/**
 * Returns the form DOM object from the current document object with the given
 * name.
 *
 * @param string Name of the form
 * @return form
 * @author Adam Nowicki
 */
function getForm(name)
{
	for ( var i = 0; i < document.forms.length; i++ )
		if ( document.forms[i].name == name )
			return document.forms[i];
}

/**
 * Toggles the display of an item between none and block.
 * Display none makes an element hidden while display block makes it visible.
 *
 * @param id of the entity.
 * @author Adam Nowicki
 */
function toggle_display(id)
{
	var style = document.getElementById(id).style;
	style.display = (style.display == "block") ? "none" : "block";
}

/**
 * Gets the value for the key specified from the GET string
 *
 * @param string Get string key
 * @return string Get string value
 * @author Adam Nowicki
 */
function getQueryVariable(key)
{
	// Takes everything after the ? in the URL
	var query = window.location.search.substring(1);
	
	// Makes an array of each key=value paris
	var vars = query.split('&');
	
	for ( var i = 0; i < vars.length; i++ )
	{
		// Splits key=value into key and value
		var pair = vars[i].split("=");
		if ( pair[0] == key )
			return pair[1];
	} 
	return null;
}

/**
 * Checks to see if a value is in the provided array and returns its position 
 * or else return -1. It skips the first offset elements if offset is provided.
 *
 * @param array The array to search
 * @param string The value to look for
 * @param integer Offset from the begining of the array.
 * @return boolean
 * @author Adam Nowicki
 */
function searchArray(array, value, offset)
{
	if ( !isInteger(offset) )
		offset = 0;
	
	if ( !isArray(array) )
		throw "Exception in searchArray() - Passed parameter is not an array!";

	for ( var i = offset; i < array.length; i++ )
		if ( array[i] == value )
			return i;
	
	return -1;
}

/**
 * Search the array using the searchArray function, but skip the provided skip
 * value of matches.
 *
 * Example:
 * a = Array(4, 3, 1, 5, 3, 6)
 * searchArraySkip(a, 3, 1)  will return 4
 *
 * @param array The array to search
 * @param string The value to look for
 * @param integer The number of matches to skip over
 * @return integer position
 * @author Adam Nowicki
 */
function searchArraySkip(array, value, skip)
{
	if ( !isInteger(skip) )
		skip = 0;
	
	if ( !isArray(array) )
		throw "Exception in searchArray() - Passed parameter is not an array!";
	
	var position = -1;
	
	while ( skip != 0 )
	{
		position = searchArray(array, value, position + 1);
		skip--;
	}
	
	position = searchArray(array, value, position + 1);
	
	return position;
}

/**
 * Count the number of occurances of value in the provided array.
 *
 * Example:
 * a = Array(3,2,47,0,33,54,3,75,3,0)
 * countSearchArray(a, "0") will return 2
 *
 * @param array The array to search
 * @param string Value to look for
 * @return integer Number of matches
 * @author Adam Nowicki
 */
function countSearchArray(array, value)
{
	if ( !isArray(array) )
		throw "Exception in countSearchArray() - Passed parameter is not an array!";
	
	var count = 0;
	
	for ( var i = 0; i < array.length; i++ )
		if ( array[i] == value )
			++count;

	return count;
}

/**
 * Checks to see if a value is in the provided array
 *
 * @param array the array
 * @param string the value
 * @return boolean
 * @author Adam Nowicki
 */
function inArray(array, value)
{
	if ( !isArray(array) )
		throw "Exception in inArray() - Passed parameter is not an array!";
	
	for ( var i = 0; i < array.length; i++ )
		if ( array[i] == value )
			return true;
	
	return false;
}

/**
 * Gets the length of the shortest string in an array
 *
 * @param array The Array
 * @return int size
 * @author Adam Nowicki
 */
function findShortestLength(array)
{
	if ( array.length == 0 )
		return false;

	var size = array[0].length;

	for ( var i = 1; i < array.length; i++ )
		if ( array[i].length < size ) size = array[i].length;
	
	return size;
}

/**
 * Returns the ASCII value of the passed character
 *
 * @param char
 * @return integer
 * @author Adam Nowicki
 */
function asciiValue(c)
{
	// restrict input to a single character
	c = c.charAt(0);

	// loop through all possible ASCII values
	var i;

	for (i = 0; i < 256; ++ i)
	{
		// convert i into a 2-digit hex string
		var h = i.toString(16);
		if (h.length == 1)
			h = "0" + h;

		// insert a % character into the string
		h = "%" + h;

		// determine the character represented by the escape code
		h = unescape(h);

		// if the characters match, we've found the ASCII value
		if (h == c)
			break;
	}
	
	return i;
}

/** 
 * Gets the unsigned value of the number passed
 *
 * @param number
 * @return number unsigned
 * @author Adam Nowicki
 */
function positiveValue(number)
{
	if ( !isNumber(number) )
		throw "Exception: function positiveValue() wasn't passed a number";
	if ( number < 0 )
		return 0 - number;
	else
		return number;
}

/**
 * Figure out what the current term is and return it as a string.  The value
 * returned is a string of the season.
 *
 * @return string Current school term
 * @author Adam Nowicki
 */
function schoolTerm()
{
	var d = new Date();
	if ( (d.getMonth == 0 && d.getDate() < 15) 
		|| (d.getMonth() == 11 && d.getDate() > 20) )
		return "Winter";
	else if ( d.getMonth() < 5 || (d.getMonth() == 5 && d.getDate() < 15) )
		return "Spring";
	else if ( d.getMonth() < 9 )
		return "Summer";
	else
		return "Fall";
}

/**
 * Returns the number of days in the current Date object based on the month
 * and year it is set to.
 *
 * @return integer Number of days
 * @author Adam Nowicki
 */
Date.prototype.daysInMonth = function()
{
	return numDaysInMonth( this.getMonth(), this.getFullYear() );
}

/**
 * Figure out the number of days in the provided month and year.
 *
 * @param integer Month
 * @param integer Year
 * @return integer Number of days
 * @author Adam Nowicki
 */
function numDaysInMonth(month, year)
{
	if ( !isInteger(month) || !isInteger(year) )
		throw "Exception: numDaysInMonth() was provided invalid parameters";
	
	var days = new Array(31, ((year % 4 == 0 && year % 100 != 0)
		|| year % 400 == 0 ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
	return days[month];
}

function xmlentities(html)
{
	//encodedHtml = escape(html);
	//var encodedHtml = encodedHtml.replace(/\//g,"%2F");
	//encodedHtml = encodedHtml.replace(/\?/g,"%3F");
	//encodedHtml = encodedHtml.replace(/=/g,"%3D");
	//encodedHtml = encodedHtml.replace(/&/g,"%26");
	//encodedHtml = encodedHtml.replace(/@/g,"%40");
	html = html.replace(/&/g, "&amp;");
	html = html.replace(/</g, "&lt;");
	html = html.replace(/>/g, "&gt;");
	return html;
} 

/**
 * Creates an HTML combo box with the name parameter set as the provided name
 * and generates the options as numbers from low parameter to high.
 *
 * @param string Name of combo
 * @param integer Number to start with
 * @param integer Number to end with
 * @return string HTML content
 * @author Adam Nowicki
 */
function createRangedComboBox(name, low, high)
{
	if ( !isNumber(low) || !isNumber(high) )
		throw "Exception: The low and/or high parameters were invalid";
		
	var combo = '<select name="' + name + '">';
	
	for ( var i = low; i <= high; i++ )
		combo += '<option value="' + i + '">' + i + '</option>';

	combo += '</select>';
	
	return combo;
}

/**
 * Defines a Vector object
 *
 * This implementation of a vector is a growable array.
 *
 * Last Verified: February 16, 2006
 * Verified By: assertions.js
 * @author Adam Nowicki
 */
function Vector(anArray, type)
{
	/**
	 * Constant representing the size to grow arrays by
	 *
	 * @var integer
	 * @access private
	 */
	var VECTOR_INC = 5;
	
	/**
	 * The size of the array
	 *
	 * @var integer
	 */
	var size = 0;
	
	/**
	 * The pointer to the current index in the vector
	 *
	 * @var integer
	 */
	var curIndex = -1;
	
	/**
	 * The array itself
	 *
	 * @var array
	 */
	var values = new Array( VECTOR_INC );
	
	/**
	 * This method takes an XML document or element and initializes a vector
	 * from it.
	 *
	 * @param xml - XML DOM Object
	 */
	this.importDocument = function(xml)
	{
		var child;
		
		// Grabs the root vector if the xml passed is a document node
		if ( xml.nodeType == DOMDocument.DOCUMENT_NODE )
			xml = xml.getElementsByTagName("vector")[0];
		
		// Gets the first item
		child = xml.firstChild;
		
		// Loop until child is null
		while ( !isNull(child) )
		{
			if ( child.nodeName == "item" )
			{
				var id = child.getAttribute("id");
				var item = child.firstChild;
				while ( !isNull(item) )
				{
					if ( item.hasChildNodes() )
					{
						if ( item.nodeName == "vector" )
						{
							var newVect = new Vector();
							newVect.importDocument(item);
							this.push( newVect );
						}
						else if ( item.nodeName == "VectorMap" )
						{
							var newVect = new VectorMap();
							newVect.importDocument(item);
							this.push(newVect);
						}
						else
							alert("BAD: " + item.nodeName);	
					}
					else
					{
						this.push( item.data );
					}
					
					item = item.nextSibling;
				}
			}	
			// Advance to the next node
			child = child.nextSibling;
		}
		
	}
	
	/**
	 * Adds a key value pair to the vector map
	 *
	 * @param string key
	 * @param string value
	 * @author Adam Nowicki
	 */
	this.push = function(value)
	{
		// Decide if we need to grow the arrays
		if ( values.length == size )
		{
			// Grow array
			var newValues = new Array ( size + VECTOR_INC );
			
			// Copy the keys and values to their new arrays
			for ( var i = 0; i < size; i++ )
				newValues[i]	= values[i];

			values = newValues;
		}
		
		// Add new value
		values[size++] = value;
	}
	
	/**
	 * Pops the last item off of the array
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.pop = function()
	{
		// Make sure the vector map isn't empty
		if ( !this.isEmpty() )
			return values[--size];
		else
			throw Vector.EXCEPTION_EMPTY;
	}
	
	/**
	 * Checks to see if the vector is empty
	 *
	 * @return boolean
	 * @author Adam Nowicki
	 */
	this.isEmpty = function()
	{
		if ( size == 0 )
			return true;
		else
			return false;
	}
	
	/**
	 * Gets the size of the vector
	 *
	 * @return integer size
	 * @author Adam Nowicki
	 */
	this.getSize = function()
	{
		return size;
	}
	
	/**
	 * Gets the value at the specified position
	 *
	 * @param int position
	 * @author Adam Nowicki
	 */
	this.get = function(position)
	{
		if ( position == null )
		{
			if ( curIndex < 0 || curIndex >= size )
				return Vector.EXCEPTION_INVALID_POSITION;
			else
				return values[ curIndex ];
		}
		else if ( position >= 0 && position < size )
			return values[position];
		else
			throw Vector.EXCEPTION_INVALID_POSITION;
	}
	
	/**
	 * Replaces value at position and return old one
	 *
	 * @param int position
	 * @author Adam Nowicki
	 */
	this.change = function(position, value)
	{
		if ( position == null || position < 0 || position >= size )
			return Vector.EXCEPTION_INVALID_POSITION;
		else
		{
			var oldValue = values[position];
			values[position] = value;
			return oldValue;
		}
	}
	
	/**
	 * Gets a copy of the values array
	 *
	 * @return array values
	 * @author Adam Nowicki
	 */
	this.getArray = function()
	{
		var newArray = new Array( size );
		
		for ( var i = 0; i < size; i++ )
			newArray[i] = values[i];

		return newArray;
	}
	
	/**
	 * Removes a key:value pair from the vector map, shifts everything left
	 * and returns the removed key:value
	 *
	 * @return string key:value
	 * @author Adam Nowicki
	 */
	this.remove = function(position)
	{
		if ( position >= size )
			throw Vector.EXCEPTION_INVALID_POSITION;
		
		var oldValue = values[position];

		for ( var i = position; i < size - 1; i++ )
			values[i] = values[i + 1];
		
		size--;
		return oldValue;
	}
	
	/**
	 * Gets the next value in the vector
	 *
	 * @return string value
	 * @author Adam Nowicki
	 */
	this.next = function()
	{
		if ( curIndex == size - 1 )
			return false;
		else
			return values[++curIndex];
	}
	
	/**
	 * Gets the previous value in the vector
	 *
	 * @return string value
	 * @author Adam Nowicki
	 */
	this.previous = function()
	{
		if ( curIndex == 0 )
			return false;
		else
			return values[--curIndex];
	}
	
	/** 
	 * Sets the current index to 0
	 *
	 * @return string first value in array
	 * @author Adam Nowicki
	 */
	this.first = function()
	{
		if ( size == 0 )
			throw Vector.EXCEPTION_EMPTY;
		else
			return values[ curIndex = 0 ];
	}
	
	/**
	 * Gets the current index position
	 *
	 * @return integer position
	 * @author Adam Nowicki
	 */
	this.getPosition = function()
	{
		return curIndex;
	}
	
	/**
	 * Sets the current index to the passed position
	 *
	 * @param integer position
	 * @author Adam Nowicki
	 */
	this.setPosition = function(position)
	{
		if ( position >= 0 && position < size )
			curIndex = position;
		else
			throw Vector.EXCEPTION_INVALID_POSITION;
	}
	
	/**
	 * Resets the index pointer to -1
	 *
	 * @author Adam Nowicki
	 */
	this.reset = function()
	{
		curIndex = -1;
	}
	
	/**
	 * Checks to see if a value exists in the vector
	 *
	 * @param string value
	 * @return boolean
	 * @author Adam Nowicki
	 */
	this.valueExists = function(value)
	{
		return inArray( this.getArray(), value );
	}
	
	/**
	 * Finds the position of the value in the vector
	 *
	 * @param string value
	 * @param integer position to start search
	 * @return integer
	 * @author Adam Nowicki
	 */
	this.search = function(value, offset)
	{
		return searchArray( this.getArray(), value, offset );
	}
	
	/**
	 * Counts the number of matches for the specified value in the vector
	 *
	 * @param string value
	 * @return integer
	 * @author Adam Nowicki
	 */
	this.countValues = function(value)
	{
		return countSearchArray( this.getArray(), value );
	}
	
	/**
	 * Finds the position of the value in the vector
	 *
	 * @param string value
	 * @param integer Number of matches to ignore
	 * @return integer
	 * @author Adam Nowicki
	 */
	this.searchSkip = function(value, skip)
	{
		return searchArraySkip( this.getArray(), value, skip );
	}
	
	/**
	 * Sorts the vector
	 *
	 * @author Adam Nowicki
	 */
	this.sort = function()
	{
		values.sort();
	}
	
	/**
	 * Sorts this vector by comparing numeric values
	 *
	 * @author Adam Nowicki
	 */
	this.sortNumeric = function()
	{
		values.sortNumeric();
	}
	
	/**
	 * Returns a XML representation of the vector
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.toFormattedString = function(tabs)
	{
		if ( tabs == null )
			tabs = 0;
		else
			tabs++;
			
		var tabstr = "";
		for ( var j = 0; j < tabs; j++ )
				tabstr += "\t";
		
		var xmlstr = "<vector>\n";
		
		for ( var i = 0; i < size; i++ )
		{	
			xmlstr += tabstr + '\t<item id="' + i + '">\n';
			xmlstr += tabstr + "\t\t" + values[i].toFormattedString(tabs + 1) + "\n";
			xmlstr += tabstr + "\t</item>\n";
		}
		xmlstr += tabstr + "</vector>\n";
		return xmlstr;
	}
	
	/**
	 * Returns a XML representation of the vector
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.toString = function()
	{	
		var xmlstr = "<vector>";
		
		for ( var i = 0; i < size; i++ )
		{	
			xmlstr += '<item id="' + i + '">';
			xmlstr += values[i].toString();
			xmlstr += "</item>";
		}
		xmlstr += "</vector>";
		return xmlstr;
	}
	
	/**
	 * Returns a string representation of the vector
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.toStringOld = function()
	{
		var str = "";
		for( var i = 0; i < size; i++ )
		{
			str += '{' + i + ':' + values[i] + '}';
		}
		return str;
	}
	
	/**
	 * Returns a document (XML DOM) object representing the vector for easy transport
	 *
	 * @return object document
	 * @author Adam Nowicki
	 */
	this.getDocument = function()
	{
		var xmldoc;
		
		if ( window.ActiveXObject )
		{
			xmldoc = new ActiveXObject("Microsoft.XMLDOM");
			xmldoc.async = false;
		}
		else if ( document.implementation.createDocument )
		{
			xmldoc = document.implementation.createDocument("","",null);
		}
		else
		{
			alert("Your browser doesn't support XML DOM");
			return;
		}
		
		var vect = document.createElement("vector");

		for ( var i = 0; i < size; i++ )
		{
			var element = document.createElement("item");
			element.setAttribute("id",i);
			var node = document.createTextNode(values[i]);
			element.appendChild(node);
			vect.appendChild(element);
		}
		xmldoc.appendChild(vect);
		return xmldoc;
	}
	
	/**
	 * Returns the type of object
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.getType = function()
	{
		return "Vector";
	}
	
	/**
	 * Clears the vector
	 *
	 * @author Adam Nowicki
	 */
	this.clear = function()
	{
		values = new Array( VECTOR_INC );
		size = 0;
		curIndex = 0;
	}
	
	/**
	 * Returns a clone of the current object
	 *
	 * @return object
	 * @author Adam Nowicki
	 */
	this.clone = function()
	{
		var newVector = new Vector();
		for ( var i = 0; i < this.getSize(); i++ )
		{
			newVector.push( this.get(i) );
		}
		
		return newVector;
	}
		
	/**
	 * Constructor
	 */
	if ( !isNull(anArray) && isArray(anArray) )
	{
		for ( var i = 0; i < anArray.length; i++ )
			this.push(anArray[i]);
	}
}

// Define exceptions for vector
Vector.EXCEPTION_NOT_FOUND 		= "VECTOR: Not found exception";
Vector.EXCEPTION_EMPTY 			= "VECTOR: Object empty exception";
Vector.EXCEPTION_INVALID_POSITION = "VECTOR: Invalid position exception";

Vector.TYPE_ANY		= "any";
Vector.TYPE_BOOLEAN	= "bool";
Vector.TYPE_INTEGER	= "int";
Vector.TYPE_STRING	= "string";
Vector.TYPE_FLOAT	= "float";
Vector.TYPE_ENUM		= "enum";
Vector.TYPE_DEFALUT	= Vector.TYPE_ANY;
/**
 * Defines a Vector Map object
 *
 * This implementation of a vector map takes the vector and adds the functionality
 * of key:value pairs.
 *
 * Last Verified: February 16, 2006
 * Verified By: assertions.js
 *
 * @author Adam Nowicki
 * @copyright 2006 University of Wisconsin Platteville
 */
function VectorMap(firstArray, secondArray, type)
{
	/**
	 * A vector of the keys
	 *
	 * @var object
	 */
	var keys = new Vector();
	
	/**
	 * A vector of the values
	 *
	 * @var object
	 */
	var values = new Vector();
	

	this.importDocument = function(xml)
	{
		var child;

		// Grabs the root vector if the xml passed is a document node
		if ( xml.nodeType == DOMDocument.DOCUMENT_NODE )
			xml = xml.getElementsByTagName("VectorMap")[0];
		
		// Gets the first item
		child = xml.firstChild;
		
		// Loop until child is null
		while ( !isNull(child) )
		{
			if ( child.nodeName == "item" )
			{
				var id = child.getAttribute("id");
				var item = child.firstChild;
				while ( !isNull(item) )
				{
					if ( item.hasChildNodes() )
					{
						if ( item.nodeName == "vector" )
						{
							var newVect = new Vector();
							newVect.importDocument(item);
							this.add( id, newVect );
						}
						else if ( item.nodeName == "VectorMap" )
						{
							var newVect = new VectorMap();
							newVect.importDocument(item);
							this.add( id, newVect );
						}
						else
							alert("BAD: " + item.nodeName);							
					}
					else
					{
						this.add( id, item.nodeValue );
					}
					
					item = item.nextSibling;
				}
			}	
			// Advance to the next node
			child = child.nextSibling;
		}
	}
	
	/**
	 * Adds a key value pair to the vector map
	 *
	 * @param string key
	 * @param string value
	 * @author Adam Nowicki
	 */
	this.add = function (key, value)
	{
		if ( this.keyExists(key) )
			throw VectorMap.EXCEPTION_KEY_EXISTS;
			
		// Add new key
		keys.push(key);
		
		// Add new value
		values.push(value);
	}
	
	/**
	 * Gets the key:value pair at the specified location (key or position)
	 *
	 * @param string key or int position
	 * @author Adam Nowicki
	 */
	this.get = function(location)
	{
		if ( this.keyExists(location) )
		{
			var position = keys.search(location);
			return keys.get(position) + ':' + values.get(position);
		}
		
		// Not a key so maybe its a position
		try
		{
			return keys.get(location) + ':' + values.get(location);
		}
		catch(e)
		{
			if ( e == Vector.EXCEPTION_INVALID_POSITION )
				return VectorMap.EXCEPTION_INVALID_KEY + " => " + location;
			else
				throw e;
		}
	}
	
	/**
	 * Replaces value at position and return old one
	 *
	 * @param int position
	 * @author Adam Nowicki
	 */
	this.change = function(location, value)
	{
		if ( this.keyExists(location) )
		{
			var position = keys.search(location);
			return values.change(position, value);
		}
		
		// Not a key so maybe its a position
		try
		{
			return values.change(location, value);
		}
		catch(e)
		{
			if ( e == Vector.EXCEPTION_INVALID_POSITION )
				return VectorMap.EXCEPTION_INVALID_KEY + " => " + location;
			else
				throw e;
		}
	}
	
	/**
	 * Gets the key at the specified position
	 *
	 * @param int position
	 * @author Adam Nowicki
	 */
	this.getKey = function(position)
	{
		return keys.get(position);
	}
	
	/**
	 * Get the value for the specified key
	 *
	 * @param string key or integer position
	 * @return string value
	 * @author Adam Nowicki
	 */
	this.getValue = function(location)
	{	
		if ( this.keyExists(location) )
			return values.get( keys.search(location) );
		
		// Not a key so maybe its a position
		try
		{
			return values.get(location);
		}
		catch(e)
		{
			if ( e == Vector.EXCEPTION_INVALID_POSITION )
			{
				//throw VectorMap.EXCEPTION_INVALID_KEY + " => " + location;
				return "";
			}
			else
				throw e;
		}
	}
	
	/**
	 * Push the value on the vector map as value => value
	 *
	 * @param string value
	 * @author Adam Nowicki
	 */
	this.push = function(value)
	{
		this.add(value, value);
	}
	
	/**
	 * Checks to see if the vector is empty
	 *
	 * @return boolean
	 * @author Adam Nowicki
	 */
	this.isEmpty = function()
	{
		return keys.isEmpty();
	}
	
	/**
	 * Get the size of the vector map
	 *
	 * @return int size
	 * @author Adam Nowicki
	 */
	this.getSize = function()
	{
		return keys.getSize();
	}
	
	/**
	 * Get the keys vector
	 *
	 * @return vector of keys
	 * @author Adam Nowicki
	 */
	this.getKeys = function()
	{
		return keys;
	}
	
	/**
	 * Get the values vector
	 *
	 * @return vector of values
	 * @author Adam Nowicki
	 */
	this.getValues = function()
	{
		return values;
	}
	
	/**
	 * Check to see if a key exists
	 *
	 * @return boolean
	 * @author Adam Nowicki
	 */
	this.keyExists = function(key)
	{
		return keys.valueExists(key);
	}
	
	/**
	 * Check to see if a value exists
	 *
	 * @return boolean
	 * @author Adam Nowicki
	 */
	this.valueExists = function(value)
	{
		return values.valueExists(value);
	}
	
	/**
	 * Check to see if a key exists and return its position
	 *
	 * @return int position / boolean false
	 * @author Adam Nowicki
	 */
	this.findKey = function(key)
	{
		return keys.search(key);
	}
	
	/**
	 * Check to see if a value exists and return its position
	 *
	 * @param string value to search for
	 * @param integer position of the array to start the search
	 * @return int position / boolean false
	 * @author Adam Nowicki
	 */
	this.findValue = function(value, offset)
	{
		return values.search(value, offset);
	}
	
	/**
	 * Returns the next key:value in the vector map
	 *
	 * @return string key:value
	 * @author Adam Nowicki
	 */
	this.next = function()
	{
		return keys.next() + ':' + values.next();
	}
	
	/**
	 * Returns the next key in the vector map
	 *
	 * @return string key
	 * @author Adam Nowicki
	 */
	this.nextKey = function()
	{
		values.next()
		return keys.next();
	}
	
	/**
	 * Returns the next value in the vector map
	 *
	 * @return string value
	 * @author Adam Nowicki
	 */
	this.nextValue = function()
	{
		keys.next();
		return values.next();
	}
	
	/**
	 * Returns the previous key:value in the vector map
	 *
	 * @return string key:value
	 * @author Adam Nowicki
	 */
	this.previous = function()
	{
		return keys.previous() + ':' + values.previous();
	}
	
	/**
	 * Returns the previous key in the vector map
	 *
	 * @return string key
	 * @author Adam Nowicki
	 */
	this.previousKey = function()
	{
		values.previous()
		return keys.previous();
	}
	
	/**
	 * Returns the previous value in the vector map
	 *
	 * @return string value
	 * @author Adam Nowicki
	 */
	this.previousValue = function()
	{
		keys.previous();
		return values.previous();
	}
	
	/**
	 * Returns the first value of the vector map
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.first = function()
	{
		keys.setPosition(0);
		values.setPosition(0);
		return values.get(0);
	}
	
	/**
	 * Resets the vector map
	 *
	 * @author Adam Nowicki
	 */
	this.reset = function()
	{
		keys.reset();
		values.reset();
	}
	
	/**
	 * Sets the position
	 *
	 * @param integer position
	 * @author Adam Nowicki
	 */
	this.setPosition = function(position)
	{
		keys.setPosition(position);
		values.setPosition(position);
	}
	
	/**
	 * Sorts the vector
	 *
	 * @param bool Sort by comparing numeric values
	 * @author Adam Nowicki
	 */
	this.sortKeys = function(numeric)
	{
		var oldKeys = keys.clone(true);
		var oldValues = values.clone(true);
		values.clear();
		
		if ( isNull(numeric) || numeric == false )
			keys.sort();
		else
			keys.sortNumeric();
		
		for ( var i = 0; i < keys.getSize(); i++ )
		{
			var oldPosition = oldKeys.search( keys.get(i) );
			var oldValue = oldValues.get( oldPosition );
			values.push( oldValue );
		}
		keys.setPosition(0);
		values.setPosition(0);
	}
	
	/**
	 * Sorts the vector
	 *
	 * @param bool Sort by comparing
	 * @author Adam Nowicki
	 */
	this.sortValues = function(numeric)
	{
		var oldKeys = keys.clone(true);
		var oldValues = values.clone(true);
		keys.clear();
		
		if ( isNull(numeric) || numeric == false )
			values.sort();
		else
			values.sortNumeric();
		
		for ( var i = 0; i < values.getSize(); i++ )
		{
			var oldPosition = oldValues.search( values.get(i) );
			var oldValue = oldkeys.get( oldPosition );
			keys.push( oldKey );
		}
		keys.setPosition(0);
		values.setPosition(0);
	}
	
	/**
	 * Removes a key:value pair from the vector map, shifts everything left
	 * and returns the removed key:value
	 *
	 * @return string key:value
	 * @author Adam Nowicki
	 */
	this.remove = function(location)
	{	
		if ( this.keyExists(location) )
		{
			var loc = keys.search(location);
			var key = keys.remove( loc );
			var value = values.remove( loc );
			
			return key + ':' + value;
		}
		
		// Not a key so maybe its a position
		try
		{
			key = keys.remove(location);
			value = values.remove(location);

			return key + ':' + value;
		}
		catch(e)
		{
			if ( e == Vector.EXCEPTION_INVALID_POSITION )
				return VectorMap.EXCEPTION_INVALID_KEY + " => " + location;
			else
				throw e;
		}
	}

	
	/**
	 * Returns a string representation of the vector
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.toFormattedString = function(tabs)
	{
		if ( tabs == null )
			tabs = 0;
		else
			tabs++;
			
		var tabstr = "";
		for ( var j = 0; j < tabs; j++ )
				tabstr += "\t";
		
		var xmlstr = "<VectorMap>\n";
		
		for ( var i = 0; i < keys.getSize(); i++ )
		{	
			xmlstr += tabstr + '\t<item id="' + keys.get(i) + '">\n';
			xmlstr += tabstr + "\t\t" + values.get(i).toFormattedString(tabs + 1) + "\n";
			xmlstr += tabstr + "\t</item>\n";
		}
		xmlstr += tabstr + "</VectorMap>\n";
		return xmlstr;
	}
	
	/**
	 * Returns a string representation of the vector without tabs and newlines
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.toString = function()
	{
		var xmlstr = "<VectorMap>";
		
		for ( var i = 0; i < keys.getSize(); i++ )
		{	
			xmlstr += '<item id="' + keys.get(i) + '">';
			xmlstr += values.get(i).toString();
			xmlstr += "</item>";
		}
		xmlstr += "</VectorMap>";
		return xmlstr;
	}

	/**
	 * Returns a string representation of the vector
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.toStringOld = function()
	{
		var str = "";
		for( var i = 0; i < keys.getSize(); i++ )
		{
			str += '[' + keys.get(i).toString() + ':'
				+ values.get(i).toString() + ']'; 
		}

		return str;
	}
	
	/**
	 * Returns a document (XML DOM) object representing the vector for easy transport
	 *
	 * @return object document
	 * @author Adam Nowicki
	 */
	this.getDocument = function()
	{
		var xmldoc;
		
		if ( window.ActiveXObject )
		{
			xmldoc = new ActiveXObject("Microsoft.XMLDOM");
			xmldoc.async = false;
		}
		else if ( document.implementation.createDocument )
		{
			xmldoc = document.implementation.createDocument("","",null);
		}
		else
		{
			alert("Your browser doesn't support XML DOM");
			return;
		}
		
		var vect = document.createElement("VectorMap");

		for ( var i = 0; i < this.size; i++ )
		{
			var element = document.createElement("item");
			element.setAttribute("id",keys[i]);
			var node = document.createTextNode(values[i]);
			element.appendChild(node);
			vect.appendChild(element);
		}
		xmldoc.appendChild(vect);
		return xmldoc;
	}

	/**
	 * Returns the type of object
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.getType = function()
	{
		return "VectorMap";
	}
	
	/**
	 * Clears the vector
	 *
	 * @author Adam Nowicki
	 */
	this.clear = function()
	{
		values.clear();
		keys.clear();
	}
	
	/**
	 * Overrides the inherrited clone method and copies private data
	 *
	 * @author Adam Nowicki
	 */
	this.clone = function()
	{
		var newVectorMap = new VectorMap();
		
		for ( var i = 0; i < keys.getSize(); i++ )
		{
			newVectorMap.add( keys.get(i), values.get(i) );
		}
		
		return newVectorMap;
	}
	
	/**
	 * Constructor
	 */
	if ( !isNull(firstArray) && !isNull(secondArray) && isArray(firstArray) && isArray(secondArray) )
	{
		if ( firstArray.length != secondArray.length )
			throw "Array lengths do not match";
		
		keys = new Vector(firstArray);
		values = new Vector(secondArray);
	}
}

// VectorMap Exceptions
VectorMap.EXCEPTION_INVALID_KEY = "VectorMap: Invalid key exception";

/**
 * Defines a Time object
 *
 * This object represents a static time.  It can be constructed with either
 * standard AM/PM or 24-hour time.  The days parameter is used when comparing
 * two times that occur in two different days.  For example, if one time was
 * set to Time("23:00"), and the other was set to Time("1:00",1), then there
 * would be 2:00 between the two values.  In that example, if the days
 * wasn't provided for the second time, the difference would be 22:00.
 *
 *	@param time - Time such as (8:00 PM or 20:00)
 * @param days - Number of days to be added to the time
 * @author Adam Nowicki
 * @copyright 2006 University of Wisconsin Platteville
 */
function Time(time, days)
{
	EXCEPTION_INVALID_HOUR = "EXCEPTION: Invalid hours";
	EXCEPTION_INVALID_MINUTE = "EXCEPTION: Invalid minutes";
	EXCEPTION_INVALID_TIME_INSTANCE = "EXCEPTION: Invalid Time instance";
	
	var hours;
	var minutes;
	var isPM;
	
	if ( time != null )
	{	
		if ( time.match(/PM$/i) )
			isPM = true;
		else if ( time.match(/AM$/i) )
			isPM = false;
		else
			isPM = null;
		
		time = time.replace(/[^0-9:]/g, "");
		time = time.split(":");
		hours = validHour( Number(time[0]) );
		
		// Add 12 to hours if PM and hours is not already 12
		if ( isPM == true && hours != 12 )
			hours += 12;
			
		// Set hours to 0 if AM and current is 12
		if ( isPM == false && hours == 12 )
			hours = 0;
			
		if ( days )
			hours += days * 24;
		minutes = validMinute( Number(time[1]) );
	}
	else
	{
		var d = new Date();
		hours = d.getHours();
		minutes = d.getMinutes();
	}
	
	/**
	 * Returns the time in standard notation unless a true is provided
	 * Examples:
	 *  timeObject.getTime() returns 4:23 PM
	 *  timeObject.getTime(true) returns 18:23
	 *
	 *	@param boolean to determine whether to provide a 24-hour time
	 * @author Adam Nowicki
	 */
	this.getTime = function(military)
	{
		// Make the minutes two digits
		if ( minutes.toString().length == 1 )
			minutes = "0" + minutes;
		
		// If not 24 hour time....
		if ( military == null || military == false )
		{
			// Set PM if greater than 12 hours
			if ( hours > 12 )
				return (hours - 12) + ":" + minutes + " PM";
			else if ( hours == 12 )
				return hours + ":" + minutes + " PM";
			else
			{
				if ( hours == 0 )
					return "12:" + minutes + " AM";
				else
					return hours + ":" + minutes + " AM";
			}
		}
		else if ( military == true )
			return hours + ":" + minutes;
	}
	
	/**
	 * Return the number of minutes for this time object
	 *
	 * @return integer
	 * @author Adam Nowicki
	 */
	this.getMinutes = function()
	{
		return minutes;
	}
	
	/**
	 * Return the number of hours for the time object
	 *
	 * @return integer
	 * @author Adam Nowicki
	 */
	this.getHours = function()
	{
		return hours;
	}
	
	/**
	 * Set the number of minutes for the time object
	 *
	 * @param integer
	 * @author Adam Nowicki
	 */
	this.setMinutes = function(min)
	{
		if ( min == validMinute(min) )
			minutes = Number(min);
		else
		{
			throw EXCEPTION_INVALID_MINUTE;
		}
	}
	
	/**
	 * Set the number of hours for the time object (in 24 hour time)
	 *
	 * @param integer
	 * @author Adam Nowicki
	 */
	this.setHours = function(hr)
	{
		if ( hr == validHour(hr) )
			hours = Number(hr);
		else
		{
			throw EXCEPTION_INVALID_HOUR;
		}
	}
	
	/**
	 * Get the period, ie either AM or PM
	 */
	this.getPeriod = function()
	{
		if ( hours >= 12 )
			return "PM";
		else
			return "AM";
	}
	
	/**
	 * Get the total number of minutes passed from 0:00
	 *
	 * @return integer
	 * @author Adam Nowicki
	 */
	this.getMinutesPassed = function()
	{
		return minutes + hours * 60;
	}
	
	/**
	 * If the second parameter, forward, is true or left empty, move time foward
	 * by given time object value. If forward is false, move time backward by
	 * the given time object's value.
	 *
	 * @param TimeObject
	 * @param boolean
	 * @return TimeObject
	 * @author Adam Nowicki
	 */
	this.timeTravel = function(time, forward)
	{
		if ( time instanceof Time )
		{
			if ( forward == null || forward == true )
				return timeOfDay(this.getMinutesPassed() + time.getMinutesPassed());
			else
				return timeOfDay(this.getMinutesPassed() - time.getMinutesPassed());
		}
		throw EXCEPTION_INVALID_TIME_INSTANCE;
	}
	
	/**
	 * Returns a string representation of the time
	 *
	 * @return string
	 * @author Adam Nowicki
	 */
	this.toString = function()
	{
		return this.getTime();
	}
	
	/**
	 * Returns true if the hours are valid
	 *
	 * @param integer
	 * @return boolean
	 * @author Adam Nowicki
	 */
	function validHour(hr)
	{
		if ( hr == 24 )
			return 0;
		else if ( hr >= 0 && hr < 24 )
			return hr;
		else if ( hr >= 24 )
		{
			while ( hr >= 24 )
				hr -= 24;
			return hr;
		}
		else
			return false;
	}
	
	/**
	 * Returns true if the minutes are valid
	 *
	 * @param integer
	 * @return boolean
	 * @author Adam Nowicki
	 */
	function validMinute(min)
	{
		if ( min >= 0 && min < 60 )
			return min;
		else
			return false;
	}
	
	/**
	 * Return a time object representative of the time of day giving the minutes
	 * that have passed since 0:00
	 *
	 * @param integer
	 * @return TimeObject
	 * @author Adam Nowicki
	 */
	function timeOfDay(minutes)
	{
		var hr = parseInt(minutes / 60);
		var min = parseInt(minutes % 60);
		var time = hr +":"+ min;
		return new Time(time);
	}
}
