//////////// BEGIN CivicPlus Controls Helper Code ////////////
// Allocates temporary thread-persistant resource. Returns name.
// To access resource, use window[] array with retVal as key.
function allocTemp(obj) {
	if (window.tempIdx == null)
		window.tempIdx = 0;
	var retVal = '__tmp' + (++window.tempIdx);
	window[retVal] = obj;
	return retVal;
}
// Frees resource from persistance array using key returned from allocTemp().
function freeTemp(key) {
	try { 
		window[key] = null;
		delete window[key];
	} catch(ex) {}
}


// Returns true on Safari browsers 1.3.4 and older (bad date object behavior in old Safari).
// Check does not work properly if user-agent has been modified by user.
var isSafariVersion13OrOlder = (isWebKit ?
	function () {
		var navAppVersion = navigator.appVersion;
		var phraseToFind = 'AppleWebKit/';
		var foundStartAt = navAppVersion.indexOf(phraseToFind) + phraseToFind.length;
		var foundEndAt = navAppVersion.indexOf(' ', foundStartAt + 1);
		return (parseInt(navAppVersion.substring(foundStartAt, foundEndAt)) < 320);
	} :
	function () {
		return false;
	}
);

// Returns true on Safari browsers 2.0.4 and older (textbox/button not stylable before 3.x).
// Check does not work properly if user-agent has been modified by user.
var isSafariVersion20OrOlder = (isWebKit ?
	function () {
		var navAppVersion = navigator.appVersion;
		var phraseToFind = 'AppleWebKit/';
		var foundStartAt = navAppVersion.indexOf(phraseToFind) + phraseToFind.length;
		var foundEndAt = navAppVersion.indexOf(' ', foundStartAt + 1);
		return (parseInt(navAppVersion.substring(foundStartAt, foundEndAt)) < 525);
	} :
	function () {
		return false;
	}
);

// Returns true if an event fired too soon after another event.
// Mechanism used on older Safari browsers to prevent 
// double-fire problems (especially with key presses).
function safariEventRateLimitBlock() {
	if (isWebKit && isSafariVersion13OrOlder()) {
		if (safariRateLimited != 0)
			return true;
		else {
			safariRateLimited = setTimeout('safariRateLimited = 0;', 10);
			return false;
		}
	}
}

//////////// BEGIN CivicPlus Date Picker Code ////////////
// Used by date picker.
var dpMonthNames = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
var dpCalendarDropStack = new Array();
var blurWorkaroundItems_dp = null;
var blurWorkaroundItemsCount_dp = null;
var blurWorkaroundItems_fcp = null;
var blurWorkaroundItemsCount_fcp = null;

// Used internally by date picker to fix annoying Safari behavior.
function addSafariBlurWorkaroundDP(item) {
	if (blurWorkaroundItems_dp == null) {
		blurWorkaroundItems_dp = new Array();
		blurWorkaroundItemsCount_dp = new Array();
		addEvent(document.body, "click", function () {
			for (var i = 0; i < blurWorkaroundItems_dp.length; i++) {
				if (blurWorkaroundItemsCount_dp[i] == 0)
					++blurWorkaroundItemsCount_dp[i];
				else
					datePickerRequestClose('safariBlurWorkaround', blurWorkaroundItems_dp[i]);
			}
		}, false);
	}
	if (!blurWorkaroundItems_dp.contains(item)) {
		blurWorkaroundItems_dp.push(item);
		blurWorkaroundItemsCount_dp.push(0);
	}
}
// Used internally by date picker to fix annoying Safari behavior.
function dropSafariBlurWorkaroundDP(item) {
	var idx = 0;
	for (var i = 0; i < blurWorkaroundItems_dp.length; i++) {
		if (blurWorkaroundItems_dp[i] == item) {
			blurWorkaroundItems_dp.splice(idx, 1);
			blurWorkaroundItemsCount_dp.splice(idx, 1);
			break;
		}
		++idx;
	}
}
// Used internally to drop date pickers from the DOM.
function datePickerDropWorker() {
	var arrElement, pickerName, inputElem;
	while(dpCalendarDropStack.length > 0) {
		arrElement = dpCalendarDropStack.pop();
		if (arrElement.parentNode) {
			arrElement.parentNode.removeChild(arrElement);
			pickerName = arrElement.id.substr(11);
			inputElem = document.getElementById(document.body.getAttribute("cpBoundInputID_" + pickerName));
			document.body.removeAttribute("cpBoundInputID_" + pickerName);
			// Special blur workaround for Safari.
			if (isWebKit)
				dropSafariBlurWorkaroundDP(pickerName);
			if (arrElement.closedEvent) {
				arrElement.closedEvent(pickerName, inputElem.value, (arrElement.newValue == true), arrElement.closedEventKey);
				arrElement.newValue = null;
			}
		}
	}
}
// Request that a date picker is closed.
function datePickerRequestClose(whoMadeRequest, pickerName) {
	var elem = document.getElementById("datePicker_" + pickerName);
	if (elem) {
		if (elem.webkitAmnestyFromDeath == 1) {
			elem.webkitAmnestyFromDeath = 0;
			return;
		}
		dpCalendarDropStack.push(elem);
	}
	//writeLog('Close requested by \'' + whoMadeRequest + '\' for date picker \'' + pickerName + '\'.');
	setTimeout("datePickerDropWorker();", 50);
}
// Abort close requests that have not occurred.
function datePickerAbortClose(pickerName) {
	//writeLog("Abort close!");
	for (var cv = 0; cv < dpCalendarDropStack.length; cv++)
		if (dpCalendarDropStack[cv].id.substr(11) == pickerName) {
			dpCalendarDropStack.splice(cv, 1);
			break;
		}
}
// Invoked when a date is selected on the date picker.
function datePickerUpdateBoundInput(pickerName, newValue) {
	var boundInput = document.getElementById(document.body.getAttribute("cpBoundInputID_" + pickerName));
	if (boundInput)
		boundInput.value = newValue;
	var pickerElement = document.getElementById('datePicker_' + pickerName);
	pickerElement.newValue = true;
	datePickerRequestClose('datePickerUpdateBoundInput()', pickerName);
	if (isWebKit)
		datePickerRequestClose('datePickerUpdateBoundInput()', pickerName);
	return false;
}
// Creates a date picker control.
// Note: closedEventKey expects key allocated from allocTemp() /w event method.
function datePickerCreate(parentElement, boundInputID, pickerName, theMonth, theYear, theSelectedDate, haveWebkitAmnesty, prevMonthButtonText, nextMonthButtonText, closedEventKey) {
	var tmpDate, theDate = new Date(theYear, --theMonth, 1);
	var theDateToday = new Date();
	document.body.setAttribute("cpBoundInputID_" + pickerName, boundInputID);
	
	var notFromInput = (document.body.getAttribute("cpBoundInputFocus_" + pickerName) != "1");
	
	// Special blur workaround for Safari.
	if (isWebKit)
		addSafariBlurWorkaroundDP(pickerName);
	
	// Specifically for WebKit/KHTML. Harmless for IE or FF.
	datePickerAbortClose(pickerName);
	
	// Safari 1.3.4 and 2.0.4 do not do dates properly or this would work. I do
	// not know if Konquerer is affected currently or not by this bug. -KB
	// tmpDate = (new Date(new Date(theDate).setMonth(theDate.getMonth() - 1)));
	tmpDate = new Date(theDate);
	if (tmpDate.getMonth() == 0) {
		tmpDate.setFullYear(tmpDate.getFullYear() - 1);
		tmpDate.setMonth(11); // December (remember these are 0-11).
	}
	else
		tmpDate = (new Date(new Date(theDate).setMonth(theDate.getMonth() - 1)));
	var prevMonth = tmpDate.getMonth();
	var prevYear = tmpDate.getFullYear();
	
	tmpDate = (new Date(new Date(theDate).setMonth(theDate.getMonth() + 1)));
	var nextMonth = tmpDate.getMonth();
	var nextYear = tmpDate.getFullYear();
	tmpDate = null;
	
	var brCount = 1;
	var notLastDay = true;
	var NextMonthDate = 1;
	var intPrintDay = 1;
	var strMonthName = dpMonthNames[theMonth];
	var intFirstWeekDay = theDate.getDay();
	var intLastDay = (new Date((new Date(theYear, theMonth + 1, 1)).setDate(0)).getDate());
	var LastMonthDate = (new Date(new Date(theDate).setDate(0))).getDate(); LastMonthDate -= (intFirstWeekDay - 1);
	var focusEventsAttr = 'onfocus="datePickerAbortClose(\'' + pickerName + '\');" onblur="datePickerRequestClose(this.id, \'' + pickerName + '\');"';
	
	var arrHtmlYearOptions = new Array();
	var todayYear = theDateToday.getFullYear();
	for (var cv = todayYear - 3; cv < todayYear + 9; cv++) {
		arrHtmlYearOptions.push('<option value="');
		arrHtmlYearOptions.push(cv);
		arrHtmlYearOptions.push('">');
		arrHtmlYearOptions.push(cv);
		arrHtmlYearOptions.push('</option>');
	}
	
	var selectedDateJS;
	if (theSelectedDate == null)
		selectedDateJS = 'null';
	else
		selectedDateJS = 'new Date(' + theSelectedDate.getFullYear() + ', ' + theSelectedDate.getMonth() + ', ' + theSelectedDate.getDate() + ')';
	
	var arrHTML = new Array(
		'		<div id="datePicker_', pickerName, '" class="DPDatePicker" style="width: 176px; overflow: hidden;" valign="top">\n',
		'			<div ', focusEventsAttr, ' class="DPDatePickerPseudoBG" style="padding: 1px; position: relative; width: 174px; text-align: center;">\n',
		'				<div class="DPMonthYearPicker" style="padding-top: 2px; padding-bottom: 2px; margin-bottom: 1px; text-align: left;">\n',
		'					<select ', focusEventsAttr, 'id="chooseMonth_', pickerName, '" style="height: 22px; margin-right: 2px; margin-left: 2px; width: 98px;" onchange="datePickerCreate(this.parentNode.parentNode.parentNode.parentNode, \'',  boundInputID, '\', \'', pickerName, '\', this.value, ',  theYear, ', ', selectedDateJS, ', true, \'' + prevMonthButtonText + '\', \'' + nextMonthButtonText + '\', \'' + closedEventKey + '\');"><option value="1">January</option><option value="2">February</option><option value="3">March</option><option value="4">April</option><option value="5">May</option><option value="6">June</option><option value="7">July</option><option value="8">August</option><option value="9">September</option><option value="10">October</option><option value="11">November</option><option value="12">December</option></select><select ', focusEventsAttr, ' id="chooseYear_', pickerName, '" style="height: 22px; margin-right: 2px; width: 70px;" onchange="datePickerCreate(this.parentNode.parentNode.parentNode.parentNode, \'',  boundInputID, '\', \'', pickerName, '\', ',  theMonth + 1, ', this.value, ', selectedDateJS, ', true, \'' + prevMonthButtonText + '\', \'' + nextMonthButtonText + '\', \'' + closedEventKey + '\');">', arrHtmlYearOptions.join(''), '</select>\n',
		'				</div>\n',
		'				<div class="DPMonthYearTitleContainer">\n',
		'					', (nextYear >= 1753 ? '<a style="z-index: 98;" class="DPMonthPrev" ' + focusEventsAttr + ' href="' + (isWebKit ? '#' : '') + '" onmousedown="datePickerCreate(this.parentNode.parentNode.parentNode.parentNode, \'' + boundInputID + '\', \'' + pickerName + '\', ' + (prevMonth + 1) + ', ' + prevYear + ', ' + selectedDateJS + ', true, \'' + prevMonthButtonText + '\', \'' + nextMonthButtonText + '\', \'' + closedEventKey + '\'); return false;" class="CalendarHeaderFont" onclick="return false;" title="Go to the Previous Month">' + prevMonthButtonText + '</a>' : ''),
		'					', (nextYear <= 3000 ? '<a style="z-index: 98;" class="DPMonthNext" ' + focusEventsAttr + ' href="' + (isWebKit ? '#' : '') + '" onmousedown="datePickerCreate(this.parentNode.parentNode.parentNode.parentNode, \'' + boundInputID + '\', \'' + pickerName + '\', ' + (nextMonth + 1) + ', ' + nextYear + ', ' + selectedDateJS + ', true, \'' + prevMonthButtonText + '\', \'' + nextMonthButtonText + '\', \'' + closedEventKey + '\'); return false;" class="CalendarHeaderFont" onclick="return false;" title="Go to the Next Month">' + nextMonthButtonText + '</a>' : ''),
		'					<div style="padding: 0px; padding-top: 1px; padding-bottom: 2px;"><input type="button" tabindex="9999" onblur="datePickerRequestClose(\'datePickerFocusHack\', \'' + pickerName + '\');" id="datePickerFocusHack_', pickerName, '" style="position: absolute; left: -1000px; top: -1000px; line-height: 0px; font-size: 0px; padding: 0px; margin: 0px; width: 1px; height: 1px; overflow: hidden; background: none; border: none;"><a ' + focusEventsAttr + ' href="' + (isWebKit ? '#' : '') + '" onclick="return false;" class="DPMonthYearTitle" style="font-weight: bold; text-decoration: none;">' + strMonthName + ', ' + theYear + '</a></div>\n',
		'				</div>\n',
		'			</div>\n',
		'			<div style="margin-top: 0px; margin-bottom; 0px; padding: 0px; border: none; background: #ffffff; position: relative;">\n',
		'				<div id="DPMonthHolder_', pickerName, '" style="position: absolute; z-index: 99; left: 3px; top: -23px; width: 98px;"></div>\n',
		'				<div id="DPYearHolder_', pickerName, '" style="position: absolute; z-index: 99; left: 104px; top: -23px; width: 68px;"></div>\n',
		'				<div ', focusEventsAttr, ' class="DPWeekdayNameList"><span ', focusEventsAttr, ' title="Sunday">S</span><span ', focusEventsAttr, ' title="Monday">M</span><span ', focusEventsAttr, ' title="Tuesday">T</span><span ', focusEventsAttr, ' title="Wednesday">W</span><span ', focusEventsAttr, ' title="Thursday">T</span><span ', focusEventsAttr, ' title="Friday">F</span><span ', focusEventsAttr, ' title="Saturday" class="DPLastDayOfWeek">S</span><br></div>\n',
		'				<div ', focusEventsAttr, ' class="DPDivider" style="width: 174px; margin-left: 1px; position: relative; padding-top: 5px; font-size: 0.1px; height: 7px; text-align: center;"><div ', focusEventsAttr, ' class="DPDividerLine" style="margin: 0px auto; text-align: center; width: 150px; font-size: 0.1px; height: 1px;"></div></div>\n');
	
	arrHTML.push('<div class="calendarGridArea" style="width: 175px; overflow: hidden; padding: 0px;">');
	while (notLastDay) {
		//('running');
		arrHTML.push('<div style="overflow: hidden; overflow: visible; margin-left: 1px; width: 176px;">');
		for (var intLoopDay = 1; intLoopDay <= 7; intLoopDay++) {
			if (intFirstWeekDay > 0) {
				arrHTML.push('<a onclick="return false;" href="');
				if (isWebKit)
					arrHTML.push('#');
				arrHTML.push('" onmousedown="return datePickerUpdateBoundInput(\'');
				arrHTML.push(pickerName);
				arrHTML.push('\', \'');
				arrHTML.push(prevMonth + 1);
				arrHTML.push('/');
				arrHTML.push(LastMonthDate);
				arrHTML.push('/');
				arrHTML.push(prevYear);
				arrHTML.push('\');" ');
				arrHTML.push(focusEventsAttr);
				arrHTML.push(' class="DPDayInactive">');
				arrHTML.push(LastMonthDate++); 
				arrHTML.push('</a>');
				--intFirstWeekDay;
			}
			else {
				if (intPrintDay > intLastDay) {
					arrHTML.push('<a onclick="return false;" href="');
					if (isWebKit)
						arrHTML.push('#');
					arrHTML.push('" onmousedown="return datePickerUpdateBoundInput(\'');
					arrHTML.push(pickerName);
					arrHTML.push('\', \'');
					arrHTML.push(nextMonth + 1);
					arrHTML.push('/');
					arrHTML.push(NextMonthDate);
					arrHTML.push('/');
					arrHTML.push(nextYear);
					arrHTML.push('\');" ');
					arrHTML.push(focusEventsAttr);
					arrHTML.push(' class="DPDayInactive');
					if (intLoopDay == 7)
						arrHTML.push(' DPLastDayOfWeek');
					arrHTML.push('">');
					arrHTML.push(NextMonthDate++); 
					arrHTML.push('</a>');
					notLastDay = false;
				}
				else {
					arrHTML.push('<a onclick="return false;" href="');
					if (isWebKit)
						arrHTML.push('#');
					arrHTML.push('" onmousedown="return datePickerUpdateBoundInput(\'');
					arrHTML.push(pickerName);
					arrHTML.push('\', \'');
					arrHTML.push(theMonth + 1);
					arrHTML.push('/');
					arrHTML.push(intPrintDay);
					arrHTML.push('/');
					arrHTML.push(theYear);
					arrHTML.push('\');" ');
					arrHTML.push(focusEventsAttr);
					arrHTML.push(' class="');
					if (theSelectedDate == null)
						arrHTML.push('DPDayActive');
					else
						arrHTML.push((new Date(theYear, theMonth, intPrintDay)).getTime() == theSelectedDate.getTime() ? 'DPDaySelected' : 'DPDayActive');
					if (intLoopDay == 7)
						arrHTML.push(' DPLastDayOfWeek');
					arrHTML.push('">');
					arrHTML.push(intPrintDay); 
					arrHTML.push('</a>');
				}
				if (++intPrintDay > intLastDay)
					notLastDay = false;
			}
		}
		if (!isie6) {
			arrHTML.push('<div style="height: 20px; font-size: 0px; margin-bottom: ');
			arrHTML.push(isie7 ? '0' : '1');
			arrHTML.push('px;"></div></div>');
		}
		else
			arrHTML.push('<br></div>');
		++brCount;
	}
	
	arrHTML.push('<div style="font-size: 1px; width: 1px; height: 1px; line-height: 0px;"></div>');
	arrHTML.push('</div>');
	arrHTML.push('\n		</div>\n	</div>');
	
	parentElement.innerHTML = arrHTML.join("");
	var pickerElement = document.getElementById('datePicker_' + pickerName);
	if (closedEventKey != null && closedEventKey != '') {
		pickerElement.closedEvent = window[closedEventKey];
		pickerElement.closedEventKey = closedEventKey;
	}
	if (isWebKit && haveWebkitAmnesty)
		pickerElement.webkitAmnestyFromDeath = 1;
	document.getElementById('chooseMonth_' + pickerName).value = theMonth + 1;
	document.getElementById('chooseYear_' + pickerName).value = theYear;
	
	// Note: Code below depends on fixedChoiceListAttach(). Only part of picker that does.
	//fixedChoiceListAttach(document.getElementById('chooseMonth_' + pickerName), document.getElementById('DPMonthHolder_' + pickerName), 'DP_' + pickerName + '_MonthHolder', 60, 21);
	//fixedChoiceListAttach(document.getElementById('chooseYear_' + pickerName), document.getElementById('DPYearHolder_' + pickerName), 'DP_' + pickerName + '_YearHolder', 60, 21);
	
	if (notFromInput && !isWebKit)
		setTimeout("document.getElementById('datePickerFocusHack_" + pickerName + "').focus();", 15);
}

function datePickerAnchorAttach(inputElement, datePickerContainerElement, pickerName, prevMonthButtonText, nextMonthButtonText, closedEventKey, anchorElemToHook) {
	var hookForClickOrFocus = function(event) {
		inputElement.select();
		document.body.setAttribute("cpBoundInputFocus_" + pickerName, "1");
		var selectedDate, dateObj = new Date();
		var parsedSenderValue = Date.parse(inputElement.value);
		if (!(parsedSenderValue >= 0 || parsedSenderValue < 0) || (parsedSenderValue == null)) {
			dateObj = new Date();
			selectedDate = null;
		}
		else {
			dateObj = new Date(parsedSenderValue);
			selectedDate = dateObj;
		}
		datePickerCreate(datePickerContainerElement, inputElement.id, pickerName, dateObj.getMonth() + 1, dateObj.getFullYear(), selectedDate, false, prevMonthButtonText, nextMonthButtonText, closedEventKey);
		// Prevent anchor from having the HREF visited.
		stopEventPropagation(event);
		return false;
	}
	// Hook click event.
	addEvent(anchorElemToHook, "click", hookForClickOrFocus, false);
}

// Attaches a date picker to a text input box.
// Side-effect Warning: Will assign an ID to elements without any previously.
// Note: closedEventKey expects key allocated from allocTemp() /w event method.
function datePickerAttach(inputElement, datePickerContainerElement, pickerName, prevMonthButtonText, nextMonthButtonText, closedEventKey) {
	// Add an ID if the input is missing it (e.g. only has a name)
	if (inputElement.id == '')
		inputElement.id = 'input_' + pickerName;
	
	var hookForClickOrFocus = function(event) {
		inputElement.select();
		document.body.setAttribute("cpBoundInputFocus_" + pickerName, "1");
		var selectedDate, dateObj = new Date();
		var parsedSenderValue = Date.parse(inputElement.value);
		if (!(parsedSenderValue >= 0 || parsedSenderValue < 0) || (parsedSenderValue == null)) {
			dateObj = new Date();
			selectedDate = null;
		}
		else {
			dateObj = new Date(parsedSenderValue);
			selectedDate = dateObj;
		}
		datePickerCreate(datePickerContainerElement, inputElement.id, pickerName, dateObj.getMonth() + 1, dateObj.getFullYear(), selectedDate, false, prevMonthButtonText, nextMonthButtonText, closedEventKey);
		return true;
	}
	// Hook focus event.
	addEvent(inputElement, "focus", hookForClickOrFocus, false);
	// Hook click event.
	addEvent(inputElement, "click", hookForClickOrFocus, false);
	
	var hookFuncKeypressOrBlur = function(event) {
		document.body.removeAttribute("cpBoundInputFocus_" + pickerName);
		datePickerRequestClose('hookFuncKeypressOrBlur()', pickerName);
		return true;
	}
	inputElement.hookFuncKeypressOrBlur = hookFuncKeypressOrBlur;
	// Hook lost focus event.
	addEvent(inputElement, "blur", hookFuncKeypressOrBlur, false);
	// Hook key press event.
	addEvent(inputElement, "keypress", hookFuncKeypressOrBlur, false);
}
//////////// END CivicPlus Date Picker Code ////////////

//////////// BEGIN CivicPlus Pop-Up Suggestion List Code ////////////
// Not using :hover due to conflict with arrow keys. Otherwise hover
// would work fine and had been working fine. -KB
var plDropStack = new Array();

// Used internally to drop pop-up lists from the DOM.
function suggestListDropWorker() {
	var arrElement, pickerName, inputElem;
	while(plDropStack.length > 0) {
		arrElement = plDropStack.pop();
		if (arrElement.parentNode) {
			arrElement.parentNode.removeChild(arrElement);
			pickerName = arrElement.id.substr(12);
			inputElem = document.getElementById(document.body.getAttribute("cpBoundInputID2_" + pickerName));
			document.body.removeAttribute("cpBoundInputID2_" + pickerName);
			if (arrElement.closedEvent) {
				arrElement.closedEvent(pickerName, inputElem.value, (arrElement.newValue == true), arrElement.closedEventKey);
				arrElement.newValue = null;
			}
		}
	}
}
// Request that a date picker is closed.
function suggestListRequestClose(pickerName) {
	var elem = document.getElementById("suggestList_" + pickerName);
	if (elem)
		plDropStack.push(elem);
	setTimeout("suggestListDropWorker();", 50);
}
// Abort close requests that have not occurred.
function suggestListAbortClose(pickerName) {
	for (var cv = 0; cv < plDropStack.length; cv++)
		if (plDropStack[cv].id.substr(12) == pickerName) {
			plDropStack.splice(cv, 1);
			break;
		}
}
// Invoked when a list item is selected on the pop-up list.
function suggestListUpdateBoundInput(pickerName, newValue) {
	var boundInput = document.getElementById(document.body.getAttribute("cpBoundInputID2_" + pickerName));
	if (boundInput) {
		if (boundInput.value != newValue) {
			boundInput.value = newValue;
			var selectedElement = document.getElementById('suggestList_' + pickerName);
			selectedElement.newValue = true;
			suggestListRequestClose(pickerName);
		}
	}
	return false;
}
// Creates the pop-up list.
function suggestListCreate(parentElement, boundInputID, pickerName, valueList, closedEventKey) {
	document.body.setAttribute("cpBoundInputID2_" + pickerName, boundInputID);
	
	// Specifically for WebKit. Harmless for IE or FF.
	suggestListAbortClose(pickerName);
	
	var selectedIndex = -1;
	var selectedValue = document.getElementById(boundInputID).value;
	var focusEventsAttr = 'onfocus="suggestListAbortClose(\'' + pickerName + '\');" onblur="suggestListRequestClose(\'' + pickerName + '\');"';
	var boundInput = document.getElementById(boundInputID);
	
	var arrHTML = new Array('		<div ', focusEventsAttr, ' id="suggestList_', pickerName, '" class="PLSuggestList" style="width: 176px;" valign="top">\n');
	for (var cv = 0; cv < valueList.length; cv++) {
		arrHTML.push('			<a ');
		arrHTML.push(focusEventsAttr);
		arrHTML.push(' href="');
		if (isWebKit)
			arrHTML.push('#');
		arrHTML.push('" id="suggestList_');
		arrHTML.push(pickerName);
		arrHTML.push('_');
		arrHTML.push(cv);
		arrHTML.push('" onmousedown="return suggestListUpdateBoundInput(\'');
		arrHTML.push(pickerName);
		arrHTML.push('\', \'');
		arrHTML.push(valueList[cv]);
		arrHTML.push('\');">');
		arrHTML.push(valueList[cv]);
		arrHTML.push('</a>\n');
		if (selectedValue == valueList[cv])
			selectedIndex = cv;
	}
	arrHTML.push('		</div>');
	parentElement.innerHTML = arrHTML.join("");
	
	// Attach close notification event.
	var selectedElement = document.getElementById('suggestList_' + pickerName);
	if (closedEventKey != null && closedEventKey != '') {
		selectedElement.closedEvent = window[closedEventKey];
		selectedElement.closedEventKey = closedEventKey;
	}
	
	// Create and attach event handlers to list items.
	var numVals = valueList.length;
	var fnEnter = (window.ActiveXObject ?
		function(event) {
			if (boundInput.ignoreMouse == true)
				boundInput.ignoreMouse = false;
			else
				suggestListHighlightByMouse(boundInput, numVals, fnEnter, event.srcElement, pickerName, true);
		} :
		function(event) {
			if (boundInput.ignoreMouse == true)
				boundInput.ignoreMouse = false;
			else
				suggestListHighlightByMouse(boundInput, numVals, fnEnter, event.target, pickerName, true);
		}
	);
	var fnMoved = (window.ActiveXObject ?
		function(event) {
			if (event.srcElement.className != 'PLSuggestListActiveItem')
				suggestListHighlightByMouse(boundInput, numVals, fnEnter, event.srcElement, pickerName, true)
			boundInput.ignoreMouse = false;
		} :
		function(event) {
			if (event.target.className != 'PLSuggestListActiveItem')
				suggestListHighlightByMouse(boundInput, numVals, fnEnter, event.target, pickerName, true)
			boundInput.ignoreMouse = false;
		}
	);
	for (var tmp, cv = 0; cv < numVals; cv++) {
		tmp = document.getElementById('suggestList_' + pickerName + '_' + cv);
		addEvent(tmp, "mouseover", fnEnter, false);
		addEvent(tmp, "mousemove", fnMoved, false);
	}
	
	// Handle selected index.
	if (selectedIndex > -1) {
		var selectedItem = document.getElementById("suggestList_" + pickerName + "_" + selectedIndex);
		if (selectedItem) {
			selectedItem.className = 'PLSuggestListActiveItem';
			var pn = selectedItem.parentNode;
			pn.scrollTop = 0;
			pn.scrollTop = selectedItem.offsetTop;
		}
	}
	
	boundInput.ignoreMouse = true;
}
// Used internally. Do not call directly in your application code.
function suggestListHighlightByMouse(boundInput, numVals, eventPtr, eventTarget, pickerName, highlightOn) {
	var cv, selectedItem;
	for (cv = 0; cv < numVals; cv++) {
		selectedItem = document.getElementById("suggestList_" + pickerName + "_" + cv);
		selectedItem.className = '';
	}
	if (highlightOn)
		eventTarget.className = 'PLSuggestListActiveItem';
}
// Used internally. Do not call directly in your application code.
function suggestListHighlightByIndex(pickerName, numVals, boundInput, indexToHighlight, scrollTo) {
	var cv, selectedItem;
	for (cv = 0; cv < numVals; cv++) {
		selectedItem = document.getElementById("suggestList_" + pickerName + "_" + cv);
		selectedItem.className = '';
	}
	selectedItem = document.getElementById("suggestList_" + pickerName + "_" + indexToHighlight);
	if (selectedItem) {
		selectedItem.className = 'PLSuggestListActiveItem';
		if (scrollTo) {
			var pn = selectedItem.parentNode;
			pn.scrollTop = 0;
			pn.scrollTop = selectedItem.offsetTop;
		}
	}
}
// Used internally. Do not call directly in your application code.
function suggestListGetHighlightedIndex(pickerName, boundInput, numVals) {
	for (var cv = 0; cv < numVals; cv++) {
		selectedItem = document.getElementById("suggestList_" + pickerName + "_" + cv);
		if (selectedItem) {
			if (selectedItem.className == 'PLSuggestListActiveItem')
				return cv;
		}
	} return -1; //boundInput.selectedIndex;
}
// Attaches a pop-up list to a text input box.
// Side-effect Warning: Will assign an ID to elements without any previously.
function suggestListAttach(inputElement, suggestListContainerElement, pickerName, valueList, closedEventKey) {
	// Add an ID if the input is missing it (e.g. only has a name)
	if (inputElement.id == '')
		inputElement.id = 'input_' + pickerName;
	
	// Hook acquire focus and click events.
	var hookForClickOrFocus = function(event) {
		inputElement.select();
		suggestListCreate(suggestListContainerElement, inputElement.id, pickerName, valueList, closedEventKey);
		return true;
	}
	addEvent(inputElement, "focus", hookForClickOrFocus, false);
	addEvent(inputElement, "click", hookForClickOrFocus, false);
	
	// Hook lost focus event.
	addEvent(inputElement, "blur", 
		function(event) {
			suggestListRequestClose(pickerName);
			return true;
		}
	, false);
	
	// Hook key down events.
	addEvent(inputElement, "keydown", 
		function(event) {
			var listContainer = document.getElementById('suggestList_' + pickerName);
			
			if (safariEventRateLimitBlock())
				return;
			
			switch(event.keyCode) {
				case 38: // Up
					if (listContainer) {
						inputElement.ignoreMouse = true;
						var idx = suggestListGetHighlightedIndex(pickerName, inputElement, valueList.length);
						if (--idx < 0) 
							idx = valueList.length - 1;
						suggestListHighlightByIndex(pickerName, valueList.length, inputElement, idx, true);
					}
					stopEventPropagation(event);
					return false;
				case 40: // Down
					if (listContainer) {
						inputElement.ignoreMouse = true;
						var idx = suggestListGetHighlightedIndex(pickerName, inputElement, valueList.length);
						if (++idx >= valueList.length)
							idx = 0;
						suggestListHighlightByIndex(pickerName, valueList.length, inputElement, idx, true);
					}
					stopEventPropagation(event);
					return false;
				case 13: // Return
					if (listContainer) {
						var idx = suggestListGetHighlightedIndex(pickerName, inputElement, valueList.length);
						suggestListUpdateBoundInput(pickerName, valueList[idx]);
						suggestListRequestClose(pickerName);
					}
					else
						suggestListCreate(suggestListContainerElement, inputElement.id, pickerName, valueList);
					stopEventPropagation(event);
					return false;
				default:
					suggestListRequestClose(pickerName);
					return true;
			}
		}
	, false);
}
//////////// END CivicPlus Pop-Up Suggestion List Code ////////////

//////////// BEGIN CivicPlus Pop-Up Custom <SELECT> List Code ////////////
var plDropStack2 = new Array();

// Used internally by date picker to fix annoying Safari behavior.
function addSafariBlurWorkaroundFCP(item) {
	if (blurWorkaroundItems_fcp == null) {
		blurWorkaroundItems_fcp = new Array();
		blurWorkaroundItemsCount_fcp = new Array();
		addEvent(document.body, "click", function () {
			for (var i = 0; i < blurWorkaroundItems_fcp.length; i++) {
				if (blurWorkaroundItemsCount_fcp[i] == 0)
					++blurWorkaroundItemsCount_fcp[i];
				else
					fixedChoiceListRequestClose(blurWorkaroundItems_fcp[i]);
			}
		}, false);
	}
	if (!blurWorkaroundItems_fcp.contains(item)) {
		blurWorkaroundItems_fcp.push(item);
		blurWorkaroundItemsCount_fcp.push(0);
	}
}
// Used internally by date picker to fix annoying Safari behavior.
function dropSafariBlurWorkaroundFCP(item) {
	var idx = 0;
	for (var i = 0; i < blurWorkaroundItems_fcp.length; i++) {
		if (blurWorkaroundItems_fcp[i] == item) {
			blurWorkaroundItems_fcp.splice(idx, 1);
			blurWorkaroundItemsCount_fcp.splice(idx, 1);
			break;
		}
		++idx;
	}
}
// Used internally to drop pop-up lists from the DOM.
function fixedChoiceListDropWorker() {
	var arrElement;
	while(plDropStack2.length > 0) {
		arrElement = plDropStack2.pop();
		pickerName = arrElement.id.substr(16);
		// Special blur workaround for Safari.
		if (isWebKit)
			dropSafariBlurWorkaroundFCP(pickerName);
		if (arrElement.parentNode)
			arrElement.parentNode.removeChild(arrElement);
	}
}
// Request that a date picker is closed.
function fixedChoiceListRequestClose(pickerName) {
	var elem = document.getElementById("fixedChoiceList_" + pickerName);
	if (elem)
		plDropStack2.push(elem);
	setTimeout("fixedChoiceListDropWorker();", 50);
}
// Abort close requests that have not occurred.
function fixedChoiceListAbortClose(pickerName) {
	for (var cv = 0; cv < plDropStack2.length; cv++)
		if (plDropStack2[cv].id.substr(16) == pickerName) {
			plDropStack2.splice(cv, 1);
			break;
		}
}
// Invoked when a list item is selected on the pop-up list.
function fixedChoiceListUpdateBoundInput(pickerName, selIndex, newValue, onChangeCallback) {
	//alert((new Array(pickerName, selIndex, newValue, onChangeCallback)).join(', '));
	var boundInput = document.getElementById(document.body.getAttribute("cpBoundInputID2_" + pickerName));
	if (boundInput)
		boundInput.value = newValue;
	var buttonElement = document.getElementById('PL_FCLS_' + pickerName);
	var newButtonVal = '';
	var isOldSafari = isSafariVersion20OrOlder();
	
	if (isOldSafari)
		newButtonVal = buttonElement.innerHTML;
	else
		newButtonVal = buttonElement.value;
	
	if (boundInput.selectedIndex > -1) {
		newButtonVal = boundInput.options[boundInput.selectedIndex].text;
		if (isOldSafari)
			buttonElement.innerHTML = newButtonVal;
		else
			buttonElement.value = newButtonVal;
	}
	
	onChangeCallback(selIndex, newButtonVal);
	fixedChoiceListRequestClose(pickerName);
	return false;
}
// Creates the pop-up list.
function fixedChoiceListCreate(parentElement, boundInputID, pickerName, pickerWidth, pickerHeight, onChangeCallback) {
	document.body.setAttribute("cpBoundInputID2_" + pickerName, boundInputID);
	
	// Specifically for WebKit. Harmless for IE or FF.
	fixedChoiceListAbortClose(pickerName);
	
	// Special blur workaround for Safari.
	if (isWebKit)
		addSafariBlurWorkaroundFCP(pickerName);
	
	var selectedIndex = -1;
	//var selectedValue = document.getElementById(boundInputID).value;
	var focusEventsAttr = 'onfocus="fixedChoiceListAbortClose(\'' + pickerName + '\');" onblur="fixedChoiceListRequestClose(\'' + pickerName + '\');"';
	var buttonElement = document.getElementById('PL_FCLS_' + pickerName);
	var boundInput = document.getElementById(boundInputID);
	
	var arrHTML = new Array('		<div ', focusEventsAttr, ' id="fixedChoiceList_', pickerName, '" class="PLFixedChoiceList" style="width: 176px;" valign="top">\n');
	var optionsArray = boundInput.options;
	for (var cv = 0; cv < optionsArray.length; cv++) {
		arrHTML.push('			<a ');
		arrHTML.push(focusEventsAttr);
		arrHTML.push(' href="');
		if (isWebKit)
			arrHTML.push('#');
		arrHTML.push('" id="fixedChoiceList_');
		arrHTML.push(pickerName);
		arrHTML.push('_');
		arrHTML.push(cv);
		arrHTML.push('" index="');
		arrHTML.push(cv);
		arrHTML.push('">');
		arrHTML.push(optionsArray[cv].text);
		arrHTML.push('</a>\n');
		if (optionsArray[cv].selected)
			selectedIndex = cv;
		
	}
	arrHTML.push('		</div>');
	parentElement.innerHTML = arrHTML.join("");
	
	// Create and attach event handlers to list items.
	var fnEnter = (window.ActiveXObject ?
		function(event) {
			if (boundInput.ignoreMouse == true)
				boundInput.ignoreMouse = false
			else
				fixedChoiceListHighlightByMouse(boundInput, fnEnter, window.event.srcElement, pickerName, true);
		} :
		function(event) {
			if (boundInput.ignoreMouse == true)
				boundInput.ignoreMouse = false
			else
				fixedChoiceListHighlightByMouse(boundInput, fnEnter, event.target, pickerName, true);
		}
	);
	var fnMoved = (window.ActiveXObject ?
		function(event) {
			if (event.srcElement.className != 'PLFixedChoiceListActiveItem')
				fixedChoiceListHighlightByMouse(boundInput, fnEnter, window.event.srcElement, pickerName, true)
			boundInput.ignoreMouse = false;
		} :
		function(event) {
			if (event.target.className != 'PLFixedChoiceListActiveItem')
				fixedChoiceListHighlightByMouse(boundInput, fnEnter, event.target, pickerName, true)
			boundInput.ignoreMouse = false;
		}
	);
	var fnChange = (window.ActiveXObject ?
		function(event) {
			var idx = event.srcElement.getAttribute("index");
			fixedChoiceListUpdateBoundInput(pickerName, idx, boundInput.options[idx].value, onChangeCallback);
			return false;
		} :
		function(event) {
			var idx = event.target.getAttribute("index");
			fixedChoiceListUpdateBoundInput(pickerName, idx, boundInput.options[idx].value, onChangeCallback);
			stopEventPropagation(event);
			return false;
		}
	);
	
	var cv, numVals = boundInput.options.length
	if (isWebKit) {
		// Safari does not do onclick with innerHTML-added elements right.
		for (var tmp, cv = 0; cv < numVals; cv++) {
			tmp = document.getElementById('fixedChoiceList_' + pickerName + '_' + cv);
			addEvent(tmp, "mouseover", fnEnter, false);
			addEvent(tmp, "mousemove", fnMoved, false);
			addEvent(tmp, "mousedown", fnChange, false);
		}
	}
	else {
		// It is crucial for Internet Explorer to capture the ONCLICK instead of MOUSEDOWN,
		// or else you won't properly override the anchor and it will send you to the homepage.
		for (var tmp, cv = 0; cv < numVals; cv++) {
			tmp = document.getElementById('fixedChoiceList_' + pickerName + '_' + cv);
			addEvent(tmp, "mouseover", fnEnter, false);
			addEvent(tmp, "mousemove", fnMoved, false);
			addEvent(tmp, "click", fnChange, false);
		}
	}
	
	// Handle selected index.
	if (selectedIndex > -1) {
		var selectedItem = document.getElementById("fixedChoiceList_" + pickerName + "_" + selectedIndex);
		if (selectedItem) {
			selectedItem.className = 'PLFixedChoiceListActiveItem';
			var pn = selectedItem.parentNode;
			pn.scrollTop = 0;
			pn.scrollTop = selectedItem.offsetTop;
		}
	}
	boundInput.ignoreMouse = true;
}
// Used internally. Do not call directly in your application code.
function fixedChoiceListHighlightByMouse(boundInput, eventPtr, eventTarget, pickerName, highlightOn) {
	var cv, selectedItem;
	for (cv = 0; cv < boundInput.options.length; cv++) {
		selectedItem = document.getElementById("fixedChoiceList_" + pickerName + "_" + cv);
		selectedItem.className = '';
	}
	if (highlightOn)
		eventTarget.className = 'PLFixedChoiceListActiveItem';
}
// Used internally. Do not call directly in your application code.
function fixedChoiceListHighlightByIndex(pickerName, boundInput, indexToHighlight, scrollTo) {
	var cv, selectedItem;
	for (cv = 0; cv < boundInput.options.length; cv++) {
		selectedItem = document.getElementById("fixedChoiceList_" + pickerName + "_" + cv);
		selectedItem.className = '';
	}
	selectedItem = document.getElementById("fixedChoiceList_" + pickerName + "_" + indexToHighlight);
	if (selectedItem) {
		selectedItem.className = 'PLFixedChoiceListActiveItem';
		if (scrollTo) {
			var pn = selectedItem.parentNode;
			pn.scrollTop = 0;
			pn.scrollTop = selectedItem.offsetTop;
		}
	}
}
// Used internally. Do not call directly in your application code.
function fixedChoiceListGetHighlightedIndex(pickerName, boundInput) {
	for (var cv = 0; cv < boundInput.options.length; cv++) {
		selectedItem = document.getElementById("fixedChoiceList_" + pickerName + "_" + cv);
		if (selectedItem) {
			if (selectedItem.className == 'PLFixedChoiceListActiveItem')
				return cv;
		}
	} return boundInput.selectedIndex;
}
// Attaches a pop-up list to a text input box.
// Side-effect Warning: Will assign an ID to elements without any previously.
function fixedChoiceListAttach(inputElement, fixedChoiceListContainerElement, pickerName, pickerWidth, pickerHeight, onChangeCallback) {
	// Add an ID if the <select> is missing it (e.g. only has a name)
	if (inputElement.id == '')
		inputElement.id = 'input_' + pickerName;
	
	// Hide the <select> tag.
	inputElement.style.visibility = 'hidden';
	inputElement.style.position = 'absolute';
	inputElement.style.left = '-2000px';
	inputElement.style.top = '-2000px';
	
	var spanElement = document.createElement("span");
	var isOldSafari = isSafariVersion20OrOlder();
	if (isOldSafari) {
		// Safari 2.x and previous are brain-dead and do not allow styling of textarea/button/textbox.
		spanElement.innerHTML = '&nbsp;<button type="button" class="PLFixedChoiceListSpawner" style="text-align: left; vertical-align: top; cursor: text;" id="PL_FCLS_' + pickerName + '" value="(Selection)">(Selection)</button>';
	}
	else 
		spanElement.innerHTML = '&nbsp;<input type="text" readonly class="PLFixedChoiceListSpawner" id="PL_FCLS_' + pickerName + '" value="(Selection)">';
	
	inputElement.parentNode.insertBefore(spanElement, inputElement.previousSibling);
	var buttonElement = document.getElementById('PL_FCLS_' + pickerName);
	
	if (pickerWidth)
		buttonElement.style.width = pickerWidth + 'px';
	if (pickerHeight)
		buttonElement.style.height = pickerHeight + 'px';
	
	// Hook acquire focus and click events.
	var hookForClickOrFocus = function(event) {
		fixedChoiceListCreate(fixedChoiceListContainerElement, inputElement.id, pickerName, pickerWidth, pickerHeight, onChangeCallback);
		return true;
	}
	addEvent(buttonElement, "focus", hookForClickOrFocus, false);
	addEvent(buttonElement, "mousedown", hookForClickOrFocus, false);
	
	// Hook lost focus event.
	addEvent(buttonElement, "blur", 
		function(event) {
			fixedChoiceListRequestClose(pickerName);
			return true;
		}
	, false);
	
	// Hook key down events.
	addEvent(buttonElement, "keydown", 
		function(event) {
			var listContainer = document.getElementById('fixedChoiceList_' + pickerName);
			
			if (safariEventRateLimitBlock())
				return;
			
			switch(event.keyCode) {
				case 37: // Left
				case 38: // Up
					if (listContainer) {
						inputElement.ignoreMouse = true;
						var idx = fixedChoiceListGetHighlightedIndex(pickerName, inputElement);
						if (--idx < 0)
							idx = inputElement.length - 1;
						fixedChoiceListHighlightByIndex(pickerName, inputElement, idx, true);
					}
					stopEventPropagation(event);
					return false;
				case 39: // Right
				case 40: // Down
					if (listContainer) {
						inputElement.ignoreMouse = true;
						var idx = fixedChoiceListGetHighlightedIndex(pickerName, inputElement);
						if (++idx >= inputElement.length)
							idx = 0;
						fixedChoiceListHighlightByIndex(pickerName, inputElement, idx, true);
					}
					stopEventPropagation(event);
					return false;
				case 13: // Return
					if (listContainer) {
						var idx = fixedChoiceListGetHighlightedIndex(pickerName, inputElement);
						fixedChoiceListUpdateBoundInput(pickerName, idx, inputElement.options[idx].value, onChangeCallback);
						fixedChoiceListRequestClose(pickerName);
					}
					else
						fixedChoiceListCreate(fixedChoiceListContainerElement, inputElement.id, pickerName, pickerWidth, pickerHeight, onChangeCallback);
					stopEventPropagation(event);
					return false;
				default:
					fixedChoiceListRequestClose(pickerName);
					return true;
			}
		}
	, false);
	
	
	if (inputElement.selectedIndex > -1) {
		var newButtonVal = inputElement.options[inputElement.selectedIndex].text;
		if (isOldSafari)
			buttonElement.innerHTML = newButtonVal;
		else
			buttonElement.value = newButtonVal;
	}
	//inputElement.selectedIndex;
}
//////////// END CivicPlus Pop-Up Custom <SELECT> List Code ////////////
