﻿function isValid(obj) { return ((typeof obj != 'undefined')&&(obj != null)); }

function getWindowHeight()
{ // gets the window's inner height ("inside" space available for content)
    var h = screen.height;
    
    if (typeof window.innerWidth != 'undefined')
    {
        //viewportwidth = window.innerWidth
        h = window.innerHeight;
    }

    // IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
    else if (typeof document.documentElement != 'undefined' && 
        typeof document.documentElement.clientWidth != 'undefined' && document.documentElement.clientWidth != 0)
    {
       //viewportwidth = document.documentElement.clientWidth
       h = document.documentElement.clientHeight;
    }
    // older versions of IE
    else
    {
       //viewportwidth = document.getElementsByTagName('body')[0].clientWidth
       h = document.getElementsByTagName('body')[0].clientHeight;
    }
    return h;
}

function resizeIFrame(frameName, resizeWidth, maxHeight, minHeight)
{ // if resizeWidth is false, then the width will not be resized
  // maxHeight is an upper limit for resizing the height of the iframe
    var rpt = _getObject(frameName);
    
    if (isValid(rpt) && !window.opera)
    {
        var getFFVersion = navigator.userAgent.substring(navigator.userAgent.indexOf("Firefox")).split("/")[1];
        var FFextraHeight = (parseFloat(getFFVersion) >= 0.1) ? 16 : 0; //extra height in px to add to iframe in FireFox 1.0+ browsers
    
        rpt.style.display = "block";
        
        var h = 0;
        
        if (rpt.contentDocument && rpt.contentDocument.body.offsetHeight) //ns6 syntax
            h = rpt.contentDocument.body.offsetHeight + FFextraHeight; 
        else if (rpt.Document && rpt.Document.body.scrollHeight) //ie5+ syntax
            h = rpt.Document.body.scrollHeight;
        
        h = (maxHeight > 0 && h > maxHeight) ? maxHeight : h;
        
        if (isValid(minHeight))
            h = (minHeight > 0 && h < minHeight) ? minHeight : h;
        
        if (isValid(h) && h > 0)
        {
            if (rpt.setAttribute)
                rpt.setAttribute("height", h);
            else if (rpt.style)
                rpt.style.height = h;
            else
                rpt.height = h;
        }
    }
}

function getFY(d) // typeof(d) == Date
{ // provides a fix for the discrepancy of Date.getYear() between IE and FireFox
    var ye = Math.round(d.getTime() / 31556952000) + 1970;
    return ye + (d.getYear() - ye) % 100;
}

function _setVisibility(objName, isVisible, useDisplayProperty)
{
	var obj = _getObject(objName);
	if(!obj) return;
	if (document.layers)
	{
		obj.visibility = (isVisible ? 'show' : 'hide');
		if(useDisplayProperty) { obj.display = (isVisible ? 'block' : 'none'); }
	}
	else
	{
		obj.style.visibility = (isVisible ? 'visible' : 'hidden');
		if(useDisplayProperty) { obj.style.display = (isVisible ? 'block' : 'none'); }
	}
}

function _isVisible(objName)
{
    var obj = _getObject(objName);
    if(! isValid(obj)) return true; // default to TRUE if object not found
    if (document.layers)
	{
		return (obj.display != 'none') && (obj.visibility == 'show' || obj.visibility == '');
	}
	else
	{
		return (obj.style.display != 'none') && (obj.style.visibility == 'visible' || obj.style.visibility == '');
	}
}

function getChildrenOfType(objParent, strType)
{	// returns an array of objects who are both contained within objParent and are of type strType
	// (which mainly refers to input objects and their type of input, whether it be radio, button, checkbox, etc.)
	var a = new Array();
	if(isValid(objParent))
	{
	    var ch = (isValid(objParent.children) ? objParent.children : objParent.childNodes);
		if(isValid(ch))
		{
			for(var i = 0; i < ch.length; i++)
			{
				if(isValid(ch[i]))
				{
					if(isValid(ch[i].type))
					{
						if(ch[i].type == strType)
							a.push(ch[i]);
					}
					var childArray = getChildrenOfType(ch[i], strType);
					for(var x = 0; x < childArray.length; x++)
						a.push(childArray[x]);
				}
			}
		}
	}
	return a;
}

function getChildrenOfTagType(tagName)
{
	var a = new Array();
    if(typeof document.getElementsByTagName != 'undefined')
        a = document.getElementsByTagName(tagName);
    else if(typeof document.all != 'undefined')
        a = document.all.tags("input");
	return a;
}

function getStyle(objectName)
{
    var styleObj = null;
    if (document.getElementById || document.all)
    {
        styleObj = _getObject(objectName).style;
    }
    else //if (document.layers)
    { // in this case, style properties are set directly on the object itself
        styleObj = _getObject(objectName);
    }
    return styleObj;
}

// internal/local variable for keeping a list of event handlers for radio button lists
var _rblHandlers = new Map();

function attachRadioButtonListClickEvent(rblName, eventHandler)
{
// eventHandler: a function with 2 parameters: radio button 'input' object, radio button value
//    handles the radio button list item click events
// rblName: the name/id of the asp.net RadioButtonList object
    // this function is used to attach an event handler to all of the 
    // (radio button) items created when using an asp.net RadioButtonList
    // the radiobutton object itself will be passed to the handler so that 
    // radiobutton.value can be accessed
/* sample usage (at the bottom of a typical aspx page with a radioButtonList having an id of 'rbl'):
    function toggleDates(radioButton)
    {
        var valu = radioButton.value;
        var bDowntime = (valu == "DowntimeReport") ? true : false;
        getStyle('divDowntime').display = (bDowntime) ? 'block':'none';
    }
    attachRadioButtonListClickEvent('rbl', toggleDates);
*/
    if((typeof eventHandler == 'function') && (rblName != null))
    {
        var rbl = _getObject(rblName);
        if(isValid(rbl))
        {
            var radios = getChildrenOfType(rbl, 'radio');
            if(isValid(radios) && (radios.length > 0))
            {
                if(! _rblHandlers.containsKey(rbl.id))
                    _rblHandlers.put(rbl.id, new Array());
                var handlerArray = _rblHandlers.get(rbl.id);
                handlerArray.push(eventHandler);
                _rblHandlers.remove(rbl.id);
	            _rblHandlers.put(rbl.id, handlerArray);
                for(var i = 0; i < radios.length; i++)
                    addEvent(radios[i], 'click', _processRadioButtonListClick);
            }
        }
    }
}
function _processRadioButtonListClick(e)
{ // a "private" function used internally for the method "attachRadioButtonListClickEvent"
// this is basically a "preprocessor" for the real radio button list 'click' event handler
// (it also supports multiple radioButtonLists on a single page as well as multiple handlers for each one)
    var ob = null;
    if(isValid(window.event) && isValid(window.event.srcElement))
        ob = window.event.srcElement;
    else if(isValid(e) && isValid(e.target))
        ob = e.target;

    if(isValid(ob) && (ob.tagName == "INPUT") && (ob.type == "radio"))
    {
        // find the "parent" radioButtonList object to retrieve the list of handlers for it
        var parent = ob.parentNode; var myId = null;
        while(isValid(parent) && (myId == null))
        {
            if(_rblHandlers.containsKey(parent.id))
                myId = parent.id;
            parent = parent.parentNode;
        }
        // if we found our parent's id, then call all handlers associated with this radioButtonList
        if(myId != null)
        {
            var handlerArray = _rblHandlers.get(myId);
            for(var i = 0; i < handlerArray.length; i++)
                if(typeof handlerArray[i] == 'function') { handlerArray[i](ob, ob.value); }
        }
    }
}

function addEvent(element, type, handler)
{
// element = reference to a document element object
// type = event type; string value e.g. 'resize' or 'load'
// handler = function reference to the event handler
	if (element.addEventListener)
		element.addEventListener(type, handler, false);
	else
	{
		if (!handler.$$guid) handler.$$guid = addEvent.guid++;
		if (!element.events) element.events = {};
		var handlers = element.events[type];
		if (!handlers)
		{
			handlers = element.events[type] = {};
			if (element['on' + type]) handlers[0] = element['on' + type];
			element['on' + type] = handleEvent;
		}
	
		handlers[handler.$$guid] = handler;
	}
}
addEvent.guid = 1;
function removeEvent(element, type, handler)
{
	if (element.removeEventListener)
		element.removeEventListener(type, handler, false);
	else if (element.events && element.events[type] && handler.$$guid)
		delete element.events[type][handler.$$guid];
}
function handleEvent(event)
{
	event = event || fixEvent(window.event);
	var returnValue = true;
	var handlers = this.events[event.type];

	for (var i in handlers)
	{
		if (!Object.prototype[i])
		{
			this.$$handler = handlers[i];
			if (this.$$handler(event) === false) returnValue = false;
		}
	}

	if (this.$$handler) this.$$handler = null;

	return returnValue;
}
function fixEvent(event)
{
	event.preventDefault = fixEvent.preventDefault;
	event.stopPropagation = fixEvent.stopPropagation;
	return event;
}
fixEvent.preventDefault = function()
{
	this.returnValue = false;
}
fixEvent.stopPropagation = function()
{
	this.cancelBubble = true;
}
// This little snippet fixes the problem that the onload attribute on the body-element will overwrite
// previous attached events on the window object for the onload event
if (!window.addEventListener)
{
	document.onreadystatechange = function()
	{
		if (window.onload && window.onload != handleEvent)
		{
			addEvent(window, 'load', window.onload);
			window.onload = handleEvent;
		}
	}
}

var _objectArray = new Map();
function _getObject(objName)
{
    var obj = _getObject_func(objName);
    if((! isValid(obj)) && (_objectArray.containsKey(objName)))
    {
        obj = _objectArray.get(objName);
        if(isValid(obj))
        {
            var obj2 = _getObject_func(obj.id);
            if((obj2 != obj) && (isValid(obj2)))
            {
	            _objectArray.remove(objName);
	            _objectArray.put(objName, obj2);
            }
        }
    }

    if(! isValid(obj))
    {
	    obj = _getObject_func(objName);
	    if(! isValid(obj))
	    {   // updated to support asp.net framework 2.0 where control names aren't what you might expect
            // (they end with the name you might expect, though)
            if(isValid(document.childNodes))
                obj = _getObject_checkNodes(document.body.childNodes, objName);
	    }
	    
	    if(_objectArray.containsKey(objName))
	        _objectArray.remove(objName);
	    _objectArray.put(objName, obj);
    }

	return obj;
}
function _getObject_func(sId)
{
    if (document.getElementById)
        return document.getElementById(sId);
    else if (document.all)
        return document.all[sId];
    else if (document.layers)
        return document.layers[sId];
    else
        return null;
}
function _getObject_checkNodes(nodes, objName)
{ // recursive method to find a tag with an ID *ending* with the specified objName
    var obj = null;
    for(var i = 0; i < nodes.length; i++)
    {
        var node = nodes[i];
        if(isValid(node))
        {
            if(isValid(node.id))
            {
                if(node.id.length >= objName.length)
                {
                    var chk = node.id.substr(node.id.length - objName.length, objName.length);
                    if(chk == objName)
                    {
                        obj = node;
                        //alert('found \'' + objName + '\' as ' + node.id);
                    }
                    //else
                    //    alert('\'' + objName + '\' not found in ' + node.id + '(' + chk + ')');
                }
            }
        }
        if((! isValid(obj)) && (isValid(node.childNodes)))
            obj = _getObject_checkNodes(node.childNodes, objName);
        if(isValid(obj))
            break;
    }
    return obj;
}

function getconfirm() 
{ 
	if (confirm("Are you sure you want to delete this record?")==true) 
		return true;
	else 
		return false;
}

var g_OpenedWindows = new Array();
var g_OWIndex = 0;

function openWin(u,W,H,X,Y,n,T,fullscreen,resize)
{ 
    var s = '';

    if (fullscreen == 1)
        s = "width=" + screen.availWidth + ",height=" + screen.availHeight;
    else
        s = "width="+W+",height="+H;
    if (resize)
	    s += ",scrollbars=1,resizable=1";
	s += ',screenX=' + ((-1 * W) - 10).toString() + ',screenY=0';
	    
    var win = window.open( u, n, s );
    try { win.blur(); top.window.focus(); } catch(ex) {}
    if(isValid(win))
    {
        if(fullscreen == 1)
            win.moveTo(0, 0);
        else
            win.moveTo(X, Y);
        win.focus();
        
        g_OpenedWindows[g_OWIndex] = new String();
        g_OpenedWindows[g_OWIndex++] = n;
    }
    else
    {
        alert(
	        'An error occurred in attempting to open a pop-up window!\n\n' + 
	        'The most common cause of this problem is the use of pop-up blocking software.\n' + 
	        'If you are not sure how to allow this website to use pop-ups, or think something\n' + 
	        'else is causing the problem, contact your system administrator.\n\n' + 
	        'Keep in mind that some browsers, like Internet Explorer, now come with pop-up blocking\n' + 
	        'features built in that are normally turned ON by default.'
        );
    }
	return true;
}

/* jgg - 12/20/05 - added to make the report window domain-specific */
var g_ReportName = 'CMSReport';
if(top.window.location.href)
{
	var tmpDomain = top.window.location.href.match( /:\/\/(www\.)?([^\/:]+)/ ); 
	tmpDomain = tmpDomain[2] ? tmpDomain[2] : '';
	if(tmpDomain)
	{
		tmpDomain = tmpDomain.replace(/\./g, '_');
		tmpDomain = tmpDomain.replace(/-/g, '_');
		g_ReportName = g_ReportName + tmpDomain;
	}
}
/* */

var g_ReportWindow = null;
function openReport(reportUrl, pleaseWaitUrl, excelReport)
{
	var reportWin;
	var width = 900;
	var height = 800;
	var sbars = 'yes';
	var bExcel = false;
	
	if(arguments.length > 2)
		bExcel = excelReport;
	
	if (!bExcel)
	{
		if (reportIsOpen(reportWin))
			return true;
	    else
	        sbars = 'none';
    }
	
	var strOptions = 'width=' + width.toString() + ',height=' + height.toString() + 
		',directories=no,location=no,menubar=' + (bExcel ? 'yes' : 'no') + 
		',resizable=yes,status=no,titlebar=no,toolbar=no,scrollbars=' + sbars;

	reportWin = window.open(reportUrl, g_ReportName, strOptions);
	
	if(isValid(reportWin))
	{
		if (reportWin.opener == null)
			reportWin.opener = window;
		
		reportWin.moveTo(0, 0);
		reportWin.resizeTo(width, height);
		
		if (pleaseWaitUrl != null)
			openPleaseWait(pleaseWaitUrl);
		
		if (!bExcel)
			g_ReportWindow = reportWin;
	}
	else
		alert(
			'An error occurred in attempting to open the report window!\n\n' + 
			'The most common cause of this problem is the use of pop-up blocking software.\n' + 
			'You must allow this website to create pop-ups as the reports are shown in new windows.\n' + 
			'If you are not sure how this can be done, or think something else is causing the problem,\n' + 
			'contact your system administrator.\n\n' + 
			'Keep in mind that some browsers, like Internet Explorer, now come with pop-up blocking\n' + 
			'features built in that are normally turned ON by default.'
		);
	
	return true;
}
function _reportOnUnload()
{
	g_ReportWindow = null;
	return true;
}
function reportIsOpen()
{ // returns a reference to a window if 
	var bOpen = false;
	var bExcel = false;
	var reportWin = null;
	
	window.focus();
	
	if (g_ReportWindow != null)
		if ((g_ReportWindow.open) && (!g_ReportWindow.closed))
		{ // if a focus() attempt fails, assume the window reference stored in the variable is invalid
			try { g_ReportWindow.focus(); bOpen = true; }
			catch (ex) { bOpen = false; }
		}
	
	if(!bOpen)
	{
		reportWin = window.open('', g_ReportName, 'top=-100,left=-100,width=1,height=1,directories=no,location=no,menubar=no,resizable=yes,status=no,titlebar=no,toolbar=no,scrollbars=none');
		try
		{
			//reportWin.moveTo(-100, -100);
			if (isValid(reportWin))
			{
				if (isValid(reportWin.document))
				{
					if (isValid(reportWin.document.location))
						if (isValid(reportWin.document.location.href))
						{
							bOpen = (reportWin.document.location.href.indexOf('http') >= 0);
						}
				}
			}
		}
		catch(ex)
		{ // if the above causes an exception, we most likely have an excel report open in a browser window
			if (isValid(reportWin))
			{
				// jgg - 6/6/05 - for some reason we were getting here with an "access denied" error (of type TypeError) 
				// when a report window was actually NOT open, so we're ignoring that specific error
				if (parseInt(ex.number & 0xFFFF) != 5)
				{
					// the "not implemented" error, happens when we have a report window open with an excel report opened within the browser
					bExcel = (parseInt(ex.number & 0xFFFF) == 16385);
					bOpen = true;
					g_ReportWindow = reportWin;
				}
				else
				{
					try { reportWin.close(); reportWin = null; g_ReportWindow = null; }
					catch (ex) { }
				}
			}
		}
	}
	else
	{
		reportWin = g_ReportWindow;
	}
	
	if(!bOpen)
	{
		if(arguments.length > 0)
			arguments[0] = reportWin;
		else if (isValid(reportWin))
			reportWin.close();
	}
	else if (reportWin == window)
	{
		// somehow the current/main window got named "cmsreport" just act as if it's not open and see if the report can open
		g_ReportWindow = null;
		reportWin = null;
		bOpen = false;
	}
	else
	{
		var sMsg = 'Another report window is open.  Please close or cancel the open report before opening a new one.'
		if (bExcel)
		{
			sMsg = sMsg + '\n\nIt has been detected that an Excel report may be open in a browser window.' + 
				'\nIf this is not the case, you may need to close all browser windows and re-run the report.';
		}
		alert(sMsg);
		try { reportWin.moveTo(0, 0); } catch (ex) { }
		try { reportWin.focus(); } catch (ex) { }
	}
		
	return bOpen;
}

function openPleaseWait(url)
{
	// the rest of the code (to resize, move, etc) SHOULD be in the pleasewait page's javascript
	// (which is why we MUST specify resizeable=1 here)
	var win = window.open(url, 'PleaseWait', 'width=1,height=1,scrollbars=0,resizable=1,left=-100,top=-100');
}

/*********************************************************************************************************************
 *  Anthem (AJAX) support code
 ********************************************************************************************************************/

function Anthem_PreCallBack()
{
    /*
    setTimeout("toggleEnable(false)", g_submittingTimeout);
    g_bDoSubmitting = true;
    _submittingTimer();
    */
}

function Anthem_PostCallBack()
{
    /*
    setTimeout("toggleEnable(true)", g_submittingTimeout);
    g_bDoSubmitting = false;
    */
}

/*********************************************************************************************************************
 *  END Anthem (AJAX) support code
 ********************************************************************************************************************/
 

/*
 * Map: a dictionary data structure based on a hash table
 * $Id: map.js 1932 2004-04-20 05:04:18Z scott $
 * Copyright (C) 2001-2003 Scott Martin (http://www.coffeeblack.org/contact/)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, it is available from the Free Software
 * Foundation, Inc. at http://www.gnu.org/copyleft/lesser.html or in writing at
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
 
/*
 * Creates a new map. This method simply initializes the internal hash table
 * backing this map. The optional argument 'size' refers to the number
 * of buckets that will be used in this map's internal hash table. If
 * this argument is unspecified, not a number, or less than one, a
 * default value of 10 buckets is used.
 *
 * A map stores a series of elements. Each element is a key and its
 * corresponding value. Once an element is added using put(), the key can then
 * be used to retrieve its corresponding value using get(). Keys must be
 * defined and non-null, and a map does not allow duplicate keys. An attempt to
 * add an element when its key is already present results in the current
 * element's value being replaced by the new one. The values themselves must be
 * defined, but can be null. Multiple duplicate values are permitted.
 *
 * Internally, a map uses a hash table to store elements inserted into it.
 * This hash table uses several buckets as storage, and the number of
 * buckets can be specified at construction. Each bucket is a chain of 
 * elements, and the appropriate bucket location of any given element is
 * determined by computing a hash code based on the element's key. The hash 
 * table implementation used by this map uses chaining to resolve collisions.
 */
function Map(size)
{	
	this.buckets = new Array();

	if(isNaN(size) || size < 1)
		size = 10;

	for(var i = 0; i < size; i++)
		this.buckets[i] = new Bucket();
}

/*
 * Stores the computed hash code for later reference.
 */
String.prototype.hashCode = -1;

/*
 * Computes a hash code for an object.
 */
String.prototype.hash = function()
{	
	with(this)
	{	
		if(hashCode == -1)
		{	
			hashCode = 0;
			var str = toString();
	
			for(var i = 0; i < str.length; i++)
				hashCode ^= str.charCodeAt(i);
		}

		return hashCode;
	}
}

/*
 * Finds the appropriate bucket for a given object using its hash code.
 */
Map.prototype.bucketFor = function(obj)
{	
	with(this)
	{	
		return buckets[obj.hash() % buckets.length];
	}
}

/*
 * Gets the size of the map, the number of elements it contains.
 */
Map.prototype.size = function()
{	
	var sz = 0;

	with(this)
	{	
		for(var i = 0; i < buckets.length; i++)
			sz += buckets[i].depth;
	}

	return sz;
}

/*
 * Tests whether this map is currently empty (has no elements).
 */ 
Map.prototype.isEmpty = function()
{	
	with(this)
	{	
		for(var i = 0; i < buckets.length; i++)
			if(buckets[i].depth > 0)
				return false;		
	}	

	return true;	
}

/*
 * Gets an array of the keys in this map.
 */
Map.prototype.keys = function()
{	
	var a = new Array();
	
	with(this)
	{	
		var bucket, e;
		for(var i = 0; i < buckets.length; i++)
		{	
			bucket = buckets[i];
			
			for(e = bucket.first; e != null; e = e.next)
				a[a.length] = e.key;
		}
	}
		
	return a;
}

/*
 * Gets an array of the values in this map.
 */
Map.prototype.values = function()
{	
	var a = new Array();
	
	with(this)
	{	
		var bucket, e;
		for(var i = 0; i < buckets.length; i++)
		{	
			bucket = buckets[i];
			
			for(e = bucket.first; e != null; e = e.next)
				a[a.length] = e.value;
		}
	}
		
	return a;
}

/*
 * Tests whether this map contains the specified key.
 */
Map.prototype.containsKey = function(key)
{	
	if(key && key != null)
	{	
		with(this)
		{	
			var bucket = bucketFor(key);

			for(var e = bucket.first; e != null; e = e.next)
				if(e.key == key)
					return true;
		}
	}
			
	return false;
}

/*
 * Tests whether this map contains the specified value.
 */
Map.prototype.containsValue = function(value)
{	
	if(value)
	{	
		with(this)
		{	
			var bucket, e;

			for(var i = 0; i < buckets.length; i++)
			{	
				bucket = buckets[i];

				for(e = bucket.first; e != null; e = e.next)
					if(e.value == value)
						return true;
			}
		}
	}
	
	return false;
}

/*
 * Adds an element to this map. The key must be defined
 * and non-null. The value must be defined, but can be null.
 *
 * If a key is currently contained in this map that is the same
 * as the key to be added, the new element is inserted in place of
 * the old one and the old element's value is then returned. If the new
 * key is not already present, this method returns null.
 */
Map.prototype.put = function(key, value)
{	
	with(this)
	{	
		return (key && key != null && value)
			? bucketFor(key).add(key, value) : null;
	}
}

/*
 * Gets the value associated with the specified key, if any.
 * Otherwise, this method returns null.
 */
Map.prototype.get = function(key)
{	
	if(key && key != null)
	{	
		with(this)
		{	
			var bucket = bucketFor(key);

			for(var e = bucket.first; e != null; e = e.next)
				if(e.key == key)
					return e.value;
		}
	}	
	
	return null;
}

/*
 * Removes the element with the specified key from this map, if any
 * such key is present. The removed element is then returned. If no such
 * key is present in this map, this method returns null. If an element is
 * successfully removed, ths size of this map decreases by 1.
 */
Map.prototype.remove = function(key)
{	
	if(key && key != null)
	{	
		with(this)
		{	
			return bucketFor(key).remove(key);
		}		
	}
	
	return null;
}


/*
 * Clears this map. This method reduces the size of the map to 0.
 */
Map.prototype.clear = function()
{	
	with(this)
	{	
		for(var i = 0; i < buckets.length; i++)
			buckets[i].clear();
	}
}

/*
 * Gets a string representation of this map.
 */
Map.prototype.toString = function()
{	
	return "[object Map]";
}

/*
 * A hash table bucket that holds entries.
 */
function Bucket()
{	// stores the number of elements contained
	this.depth = 0;
	// the first element in the chain
	this.first = null;
}

/*
 * Adds an entry to this bucket. If an entry already existed with the same
 * key, its value is replaced and the old value is then returned.
 */
Bucket.prototype.add = function(key, value)
{	
	with(this)
	{	
		if(first != null) // look for the key
		{	
			for(var e = first; e != null; e = e.next)
			{	
				if(e.key == key) // same key already present
				{	
					var old = e.value;
					e.value = value;
					return old;
				}
			}			
		}

		// bucket empty or key not present, add a new entry
		first = new Entry(key, value, first);
		depth++;
	}

	return null;
}

/*
 * Removes an entry from this bucket. If an entry was removed,
 * its value is returned and the bucket's depth is decreased by one.
 */
Bucket.prototype.remove = function(key)
{	
	with(this)
	{	
		if(first != null)
		{	
			for(var e = first, prev = null; e != null; prev = e, e = e.next)
			{	
				if(e.key == key)
				{	
					if(prev == null) // it's the first entry
						first = e.next;
					// otherwise, link previous to next
					else prev.next = e.next;

					depth--;
					return e.value;
				}
			}
		}		
	}
	
	return null;
}

/*
 * Removes all entries from this bucket.
 */
Bucket.prototype.clear = function()
{	
	with(this)
	{	
		first = null;
		depth = 0;
	}
}

/*
 * Gets a string representation of this bucket.
 */
Bucket.prototype.toString = function()
{	
	return "[object Bucket]";
}

/*
 * A map entry. This object simply ties a key to its associated value.
 * An entry also stores a reference to the next object in the chain of
 * entries in the bucket they belong to.
 */
function Entry(k, val, nxt)
{	
	this.key = k;
	this.value = val;
	this.next = nxt ? nxt : null;
}

/*
 * Gets a string representation of this entry.
 */
Entry.prototype.toString = function()
{	
	return "[object Entry]";
}