// Vovici Client Side Survey API
// Copyright 1997-2009 Vovici Corporation. All rights reserved.
// Version: 5.2.0

// Implement swapNode for older browsers
try {
	if (!Node.prototype.swapNode) {
		Node.prototype.swapNode = function(node) {
			var nextSibling = this.nextSibling;
			var parentNode = this.parentNode;
			node.parentNode.replaceChild(this, node);
			parentNode.insertBefore(node, nextSibling);
		}
	}
} catch (e) { }

/// <summary>
/// Creates a new instance of the Client Side Survey API.
/// </summary>
function VcSurvey() {
	this.Culture = 'en-US';
	// Number Formatting Defaults
	this.NumberDecimalSeparator = '.';
	this.NumberGroupSeparator = ',';
	this.NumberGroupSizes = 3;
	// Date Picker Defaults
	this.PrevText = 'Back';
	this.NextText = 'Next';
	this.MonthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
	this.MonthNamesShort = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
	this.DayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
	this.DayNamesShort = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
	this.DayNamesMin = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
	this.DateFormat = 'mm/dd/yyyy';
	this.DateSeparator = '/'
	this.FirstDay = 0;
	this.IsRTL = false;	
	this.firstValidationMessage = null;
	this.validationMessages = new Object();
	this.AddEvent(window, "load", this.DisableEnterKey);
}

/// <summary>
/// Returns the form element containing the survey.
/// </summary>
VcSurvey.prototype.SurveyForm = function() {
	var form = document.getElementById("PdcSurvey");
	if (!form) form = document.forms["PdcSurvey"];
	return form;
};

/// <summary>
/// Gets a value from the query string and returns it or blank if not found.
/// </summary>
// <param name="parameterName">A valid url parameter</param>
VcSurvey.prototype.GetUrlParameter = function(parameterName) {
	// Get the query string minus the ?
	var queryString = location.search.substring(1);

	// Convert plus to space
	queryString = queryString.replace(/\+/g, " ");
	// Split the args by & and the look at each named pair.
	var params = queryString.split('&');
	for (var x = 0; x < params.length; x++) {
		var pair = params[x].split('=');
		if ((pair.length == 2) && (pair[0] == parameterName)) {
			return decodeURIComponent(pair[1]);
		}
	}

	return '';
};

/// <summary>
/// Gets a value from the query string and sets it to the
/// specified fill in the blank, essay or hidden question.
/// </summary>
// <param name="heading">A valid dbHeading</param>
// <param name="parameterName">A valid url parameter</param>
VcSurvey.prototype.SetUrlParameter = function(heading, parameterName) {
	this.SetTextValue(heading, this.GetUrlParameter(parameterName));
};

/// <summary>
/// Auto Advances the survey to the next page,
/// or submits if on the last page.
/// </summary>
VcSurvey.prototype.AutoAdvance = function() {
	if (document.PdcSurvey.next) {
		document.PdcSurvey.next.click();
	} else if (document.PdcSurvey.submit) {
		document.PdcSurvey.submit.click();
	} else {
		document.PdcSurvey.submit();
	}
};

/// <param>
/// Sets html to an element.
/// </param>
/// <param name="id">Element ID</param>
/// <param name="html">HTML to assign</param>
VcSurvey.prototype.SetElementHtml = function(id, html) {
	var element = document.getElementById(id);
	if (element) element.innerHTML = html;
};

/// <summary>
/// Returns the value for a fill in the blank or essay question as a string.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.GetTextValue = function(heading) {
	var value = '';
	var inputField = document.getElementById(heading);
	if (inputField) value = inputField.value;
	return value.toString();
};

/// <summary>
/// Sets the value for a fill in the blank, essay or hidden question.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="newValue">Value to assign to field</param>
VcSurvey.prototype.SetTextValue = function(heading, newValue) {
	var inputField = document.getElementById(heading);
	if (inputField) inputField.value = newValue;
};

/// <summary>
/// Returns the value for a fill in the blank as number.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.GetNumericValue = function(heading) {
	var textValue = this.GetTextValue(heading);
	var numValue = this.ToEnglishNumber(textValue);
	return Number(numValue);
};

/// <summary>
/// Returns the selected value of a Choose One
/// (Radio Button List or Select List) or 0 for Not Answered.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.GetChooseOneValue = function(heading) {
	var elements = document.getElementsByName(heading);
	for (var x = 0; x < elements.length; x++) {
		element = elements[x];
		if (element.tagName == "SELECT") {
			return Number(element.options[element.selectedIndex].value);
		} else if (element.type == "radio" && element.checked) {
			return Number(element.value);
		}
	}
	return 0;
};

/// <summary>
/// Sets the value for a Choose One
/// (Radio Button List or Select List)
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="newValue">Value to assign to field</param>
VcSurvey.prototype.SetChooseOneValue = function(heading, newValue) {
	var elements = document.getElementsByName(heading);
	for (var x = 0; x < elements.length; x++) {
		element = elements[x];
		if (element.tagName == "SELECT") {
			var selectedIndex = 0;
			var optionCount = element.options.length - 1;
			for (var i = 1; i < optionCount; i++) {
				if (element.options[i].value == newValue) {
					selectedIndex = i;
					break;
				}
			}
			element.selectedIndex = selectedIndex;
			return;
		} else if (element.type == "radio") {
			element.checked = (element.value == newValue);
		}
	}
};

/// <summary>
/// Returns the selected value of a Choose Many choice
/// 0 = unchecked, 1 = checked.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.GetChooseManyValue = function(heading) {
	var element = document.getElementById(heading);
	if (element && element.checked) return 1;
	return 0;
};

/// <summary>
/// Sets the value for a Choose Many choice.
/// </summary>
/// <param name="newValue">Value to assign to field 0 = unchecked, 1 = checked</param>
VcSurvey.prototype.SetChooseManyValue = function(heading, newValue) {
	var element = document.getElementById(heading);
	if (element) element.checked = (newValue == 1);
};

/// <summary>
/// Given a list of choose many choices, sets them to not checked.
/// </summary>
/// <param>List of dbHeadings e.g. 'Q1_1', 'Q1_2', 'Q1_3'</param>
VcSurvey.prototype.ClearCheckboxes = function() {
	for (var i = 0; i < arguments.length; i++) {
		var element = document.getElementById(arguments[i]);
		if (element) element.checked = false;
	}
};

/// <summary>
/// Indicates if the specified essay question, fill in the blank topic
/// or choose one list/dropdown has been answered. Or if the specified choose one
/// radio or choose many checkbox have been selected.
/// Does not indicate whether its a valid answer.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.IsAnswered = function(heading) {
	var inputField = document.getElementById(heading);
	if (inputField) {
		switch (inputField.tagName) {
			// Fill In
			case "INPUT":
				switch(inputField.type) {
					case "text":
					case "password":
					case "hidden":
						var ghostText = inputField.getAttribute("ghostText");
						if (ghostText && ghostText == inputField.value) return false;
						return (inputField.value != '');
					case "radio":
					case "checkbox":
						return inputField.checked;
				}
				break;
			// Essay
			case "TEXTAREA":
				return (inputField.value != '');
			// Choose One List/Dropdown
			case "SELECT":
				var value = inputField.options[inputField.selectedIndex].value;
				return (value != '0' && value != '');
		}
	} else {
		if (document.getElementById(heading + "_DD")) {
			var monthElement = document.getElementById(heading + "_MM");
			var month = Number(monthElement.options[monthElement.selectedIndex].value);
			var day = Number(document.getElementById(heading + "_DD").value);
			var year = Number(document.getElementById(heading + "_YYYY").value);
			if (month != 0 || day != 0 || year != 0) return true;
		}
	}
	return false;
};

/// <summary>
/// Given a list of field headings, returns the number
/// of them that have been answered.
/// </summary>
/// <param>List of dbHeadings e.g. 'Q1_1', 'Q1_2', 'Q1_3'</param>
VcSurvey.prototype.AnsweredCount = function() {
	var answerCount = 0;
	for (var x = 0; x < arguments.length; x++) {
		if (this.IsAnswered(arguments[x])) answerCount++;
	}
	return answerCount;
};

/// <summary>
/// Given a list of field headings, returns true
/// if any of them have been answered.
/// </summary>
/// <param>List of dbHeadings e.g. 'Q1_1', 'Q1_2', 'Q1_3'</param>
VcSurvey.prototype.AnyAnswered = function() {
	for (var x = 0; x < arguments.length; x++) {
		if (this.IsAnswered(arguments[x])) return true;
	}
	return false;
};

/// <summary>
/// Given a list of field headings, returns true
/// if any of them have not been answered.
/// </summary>
/// <param>List of dbHeadings e.g. 'Q1_1', 'Q1_2', 'Q1_3'</param>
VcSurvey.prototype.AnyNotAnswered = function() {
	for (var x = 0; x < arguments.length; x++) {
		if (!this.IsAnswered(arguments[x])) return true;
	}
	return false;
};

/// <summary>
/// Given a fill in the blank heading, returns true if the length
/// of the respondents answer is less than the specified length.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="length">Minimum Length</param>
VcSurvey.prototype.IsMinLength = function(heading, length) {
	return (this.GetTextValue(heading).length < length);
};

/// <summary>
/// Given a fill in the blank heading, returns true if the length
/// of the respondents answer is greater than the specified length.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="length">Maximum Length</param>
VcSurvey.prototype.IsMaxLength = function(heading, length) {
	return (this.GetTextValue(heading).length > length);
};

/// <summary>
/// Given a fill in the blank heading, returns true if the value
/// of the respondents answer is within than the specified range.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="minValue">Minimum Value</param>
/// <param name="maxValue">Maximum Value</param>
VcSurvey.prototype.IsInRange = function(heading, minValue, maxValue) {
	var numValue = this.GetNumericValue(heading);
	if (minValue != null && numValue < minValue) return false;
	if (maxValue != null && numValue > maxValue) return false;
	return true;
};

/// <summary>
/// Given a list of field headings, returns true
/// if they all have unique values.
/// </summary>
/// <param>An array object of dbHeadings e.g. new Array('Q1_1', 'Q1_2', 'Q1_3')</param>
VcSurvey.prototype.IsRankedOrder = function(headings) {
	var choiceValues = new Array();
	for (var x = 0; x < headings.length; x++) {
		var value = this.GetChooseOneValue(headings[x]);
		// Exclude Not Answered and Coded Values
		if (value > 0) {
			// If value already selected
			if (choiceValues[value] == 1) {
				return false;
			} else {
				choiceValues[value] = 1;
			}
		}
	}
	return true;
};

/// <summary>
/// Indicates if the specified fill in topic contains a whole number.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.IsInteger = function(heading) {
	var inputField = document.getElementById(heading);
	if (inputField) return this.IsTextInteger(inputField.value);
	return false;
};

/// <summary>
/// Indicates if the specified text is a whole number.
/// </summary>
/// <param name="text">Text being tested</param>
VcSurvey.prototype.IsTextInteger = function(text) {
	// If number contains both a group seperator and a decimal seperator
	// We can determine if they entered the number for the wrong region.
	var groupSep = text.indexOf(this.NumberGroupSeparator);
	// If seperator is ascii 160 space, and was not found, also search for ascii 32 space
	if (groupSep == -1 && this.NumberGroupSeparator == "\u00A0") {
		groupSep = text.indexOf("\ ");
	}
	var decimalSep = text.indexOf(this.NumberDecimalSeparator);
	if (groupSep != -1 && decimalSep != -1 && (groupSep > decimalSep)) return false;
	// Test Concurrent Separators
	var reGroup = new RegExp("\\" + this.NumberGroupSeparator + "{2,}");
	var reDecimal = new RegExp("\\" + this.NumberDecimalSeparator + "{2,}");
	if(reGroup.exec(text) != null || reDecimal.exec(text) != null) return false;
	var number = this.ToEnglishNumber(text);
	if (number.match(/^-?\d+$/)) return true;
	return false;
};


/// <summary>
/// Indicates if the specified fill in topic contains a real number.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.IsFloat = function(heading) {
	var inputField = document.getElementById(heading);
	if (inputField) return this.IsTextFloat(inputField.value);
	return false;
};

/// <summary>
/// Indicates if the specified text is a real number.
/// </summary>
/// <param name="text">Text being tested</param>
VcSurvey.prototype.IsTextFloat = function(text) {
	// If number contains both a group seperator and a decimal seperator
	// We can determine if they entered the number for the wrong region.
	var groupSep = text.indexOf(this.NumberGroupSeparator);
	// If seperator is ascii 160 space, and was not found, also search for ascii 32 space
	if (groupSep == -1 && this.NumberGroupSeparator == "\u00A0") {
		groupSep = text.indexOf("\ ");
	}
	var decimalSep = text.indexOf(this.NumberDecimalSeparator);
	if (groupSep != -1 && decimalSep != -1 && (groupSep > decimalSep)) return false;
	// Test Concurrent Separators
	var reGroup = new RegExp("\\" + this.NumberGroupSeparator + "{2,}");
	var reDecimal = new RegExp("\\" + this.NumberDecimalSeparator + "{2,}");
	if(reGroup.exec(text) != null || reDecimal.exec(text) != null) return false;
	var number = this.ToEnglishNumber(text);
	if (number.match(/^-?\d*(\.\d+)?$/)) return true;
	return false;
};

/// <summary>
/// Indicates if the specified fill in topic contains a valid e-mail address.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.IsEmail = function(heading) {
	var inputField = document.getElementById(heading);
	if (inputField) {
		var email = inputField.value;
		if (email.match(/^[\w_\-\.\']+[\%\+]?[\w_\-\']*\@[0-9a-zA-Z\-]+\.[0-9a-zA-Z\-\.]+$/)) return true;
	}
	return false;
};

/// <summary>
/// Indicates if the specified fill in topic contains a valid date.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.IsDate = function(heading) {
	// Get Month/Day/Year Values
	var month, day, strYear;
	var dateElement = document.getElementById(heading);
	// Calendar Picker
	if (dateElement) {
		var dateValue = dateElement.value;
		var dateFormat = this.DateFormat;
		// Croatian, Hungarian, Latvian end with trailing period
		if(dateValue.lastIndexOf('.') == dateValue.length - 1) {
			dateValue = dateValue.substr(0, dateValue.length - 1);
		}
		if(dateFormat.lastIndexOf('.') == dateFormat.length - 1) {
			dateFormat = dateFormat.substr(0, dateFormat.length - 1);
		}
		var reGroup = new RegExp("\\" + this.DateSeparator);
		// Uzbekistan uses different separators
		if (this.Culture == "uz-Latn-UZ") {
			reGroup = new RegExp("\\/| ");
		}
		var dateParts = dateFormat.split(reGroup);
		var valueParts = dateValue.split(reGroup);
		if (valueParts.length < 3) return false;
		for (var i = 0; i < 3; i++) {
			if (dateParts[i] == "mm") month = Number(valueParts[i]);
			if (dateParts[i] == "dd") day = Number(valueParts[i]);
			if (dateParts[i] == "yyyy") strYear = valueParts[i];
		}
	// Multiple Controls
	} else {
		var monthElement = document.getElementById(heading + "_MM");
		month = Number(monthElement.options[monthElement.selectedIndex].value);
		day = Number(document.getElementById(heading + "_DD").value);
		strYear = document.getElementById(heading + "_YYYY").value;
	}
	var year = Number(strYear);
	// Not a number
	if (isNaN(month) || isNaN(day) || isNaN(year)) return false;
	// Month out of range
	if (month < 1 || month > 12) return false;
	// Day out of range
	if (day < 1 || day > 31) return false;
	// 30 Day Months
	if ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) return false;
	// Leap Year Check
	if (month == 2) {
		var isleap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
		if (day > 29 || (day == 29 && !isleap)) return false;
	}
	// Years must be a positive 4 digit integer.
	if (year <= 0 || strYear.length != 4 || !this.IsTextInteger(strYear)) return false;
	return true;
};

/// <summary>
/// Private - Given an array of field headings, returns their total as a number.
/// </summary>
/// <param name="heading">Array of dbHeadings</param>
VcSurvey.prototype.calculateTotal = function(headings) {
	var total = 0;
	var decimalPlaces = 0;

	for (var i = 0; i < headings.length; i++) {
		var heading = headings[i];
		var inputField = document.getElementById(heading);
		if (inputField) {
			var number = this.ToEnglishNumber(inputField.value);
			// Add leading 0 if missing
			if (number.indexOf(".") == 0) number = "0" + number;
			// Get decimal precision
			var decimalIndex = number.indexOf(".");
			if (decimalIndex != -1) {
				var decimalPlacesCount = number.length - (decimalIndex + 1);
				if (decimalPlacesCount > decimalPlaces) decimalPlaces = decimalPlacesCount;
			}
			if (number.match(/^-?\d+(\.\d+)?$/)) {
				total += parseFloat(number);
			}
		}
	}

	if (decimalPlaces > 0) total = total.toFixed(decimalPlaces);

	return total;
};

/// <summary>
/// Given a list of field headings, returns their total as a number.
/// </summary>
/// <param>List of dbHeadings e.g. 'Q1_1', 'Q1_2', 'Q1_3'</param>
VcSurvey.prototype.NumericTotal = function() {
	return this.calculateTotal(arguments);
};

/// <summary>
/// Given a list of field headings, returns their total as a string
/// formatted for the surveys culture.
/// </summary>
/// <param>List of dbHeadings e.g. 'Q1_1', 'Q1_2', 'Q1_3'</param>
VcSurvey.prototype.StringTotal = function() {
	var total = this.calculateTotal(arguments);
	return this.ToRegionNumber(total);
};


/// <summary>
/// Displays the running total for a list of topics.
/// </summary>
/// <param>
/// First arguement is dbheading of the question where to display the total.
/// Second arguement is list of fields to add.
/// e.g. 'Q1', 'Q1_1', 'Q1_2', 'Q2_3', 'Q2_4'
/// </param>
/// <param>List of dbHeadings e.g. 'Q1_1', 'Q1_2', 'Q1_3'</param>
VcSurvey.prototype.ShowRunningTotal = function() {
	var heading = arguments.pop() + "TOTAL";
	var element = document.getElementById(heading);
	if (element) element.innerHTML = this.StringTotal(arguments);
};

/// <summary>
/// Converts a number to a string with regional formatting.
/// </summary>
/// <param name="number">Number</param>
VcSurvey.prototype.ToRegionNumber = function(number) {
	var negative = (number < 0);

	// Convert to string
	number = number.toString();

	// Insert group seperators
	var parts = number.split('.');
	var base = Math.abs(parts[0]).toString();
	var groups = [];
	while(base.length > this.NumberGroupSizes) {
		var temp = base.substr(base.length - this.NumberGroupSizes);
		groups.unshift(temp);
		base = base.substr(0, base.length - this.NumberGroupSizes);
	}
	if(base.length > 0) { groups.unshift(base); }
	base = groups.join(this.NumberGroupSeparator);

	// Add decimal places if any with decimal symbol
	if (parts.length > 1) {
		number = base + this.NumberDecimalSeparator + parts[1];
	} else {
		number = base;
	}

	// Add minus sign if negative value
	if (negative) number = "-" + number;

	return number;
};

/// <summary>
/// Converts a region based number to US English Format.
/// </summary>
/// <param name="number">Number string for survey region</param>
VcSurvey.prototype.ToEnglishNumber = function(number) {
	while (number.indexOf(this.NumberGroupSeparator) != -1) {
		number = number.replace(this.NumberGroupSeparator, "");
	}
	// If seperator is ascii 160 space, also strip out ascii 32 space
	if (this.NumberGroupSeparator == "\u00A0") {
		while (number.indexOf("\ ") != -1) {
			number = number.replace("\ ", "");
		}
	}
	number = number.replace(this.NumberDecimalSeparator, ".");
	return number;
};

/// <summary>
/// Apply Odd/Even row style to a Matrix Question
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.DisplayOddEvenRows = function(heading) {
	var table = document.getElementById("T" + heading);
	if (table) {	
		var tbody = table.getElementsByTagName("TBODY");
		if (tbody) {
			var viscount = 0;
			var rows = tbody[0].getElementsByTagName("TR");			
			for(var i = 0; i < rows.length; i++) {
				var row = rows[i];
				if (row.style.display != "none") {
					viscount++;
					row.className = (viscount % 2 == 1) ? "odd-row" : "even-row";
				}
			}
		}
	}
};

/// <summary>
/// Hides columns (choices) in a table question based on a previous choose many's answers
/// </summary>
/// <param>
/// First arguement is value to test 1 for selected, 0 for not selected
/// Remaining arguements are the dependent value (Usually a placeholder e.g. %Q1_1%) and the column id.
/// e.g. 1, '1|Q2_A_C1', '0|Q2_A_C2', '0|Q2_A_C3', '1|Q2T4', '0|Q2_A_C4'
/// </param>
VcSurvey.prototype.DisplayDynamicColumns = function() {
	var value = arguments[0];
	for (var i = 1; i < arguments.length; i++) {
		var pair = arguments[i].split('|');
		var cells = document.getElementsByName(pair[1]);
		if (pair[0] != value) {
			for(j = 0; j < cells.length; j++) {
				cells[j].style.display = "none";
			}
		}
	}
};

/// <summary>
/// Gets whether the specified table question topic row is visible.
/// </summary>
/// <param name="elementId">Id of table row element.</param>
VcSurvey.prototype.IsTopicVisible = function(elementId) {
	var element = document.getElementById(elementId);
	return (element && element.style.display=="");
};

/// <summary>
/// Hides choices in a table question dropdown based on a previous choose many's answers
/// </summary>
/// <param>
/// First arguement is value to test 1 for selected, 0 for not selected
/// Second arguement is the dropdown prefix
/// Third arguement is number of topics
/// Remaining arguements are the dependent values (Usually a placeholder e.g. %Q1_1%).
/// e.g. 1, 'Q3_A', 5, '1', '0', '0', '1', '0'
/// </param>
VcSurvey.prototype.DisplayDynamicList = function() {
	var value = arguments[0];
	var tableSide = arguments[1];
	var topicCount = arguments[2];
	for (var i = 1; i <= topicCount; i++) {
		var dbHeading = tableSide + "_" + i;
		for (var j = 3; j < arguments.length; j++) {
			if (arguments[j] != value) {
				var choice = j - 2;
				this.RemoveListChoice(dbHeading, choice);
			}
		}
	}
};

/// <summary>
/// Removes all choices with the specified value from a dropdown or list box.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="choiceValue">Value of choice to remove</param>
VcSurvey.prototype.RemoveListChoice = function(heading, choiceValue) {
	var choiceList = document.getElementById(heading);
	var optionCount = choiceList.options.length - 1;

	for (var i = optionCount; i > 0; i--) {
		if (choiceList.options[i].value == choiceValue) choiceList.options[i] = null;
	}
};

/// <summary>
/// Randomizes the Choices in a Choose One List Box or Dropdown Question.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.RandomizeListChoices = function(heading) {
	var selectedValue = this.GetChooseOneValue(heading);	
	var choiceList = document.getElementById(heading);
	var optionCount = choiceList.options.length - 1;
	var randomizedChoices = new Array();

	for (var i = 1; i <= optionCount; i++) {
		var option = choiceList.options[i];
		var fixed = option.getAttribute("fixed");
		if (!(fixed && fixed == "fixed")) {
			choiceList.options[i] = new Option(i, i);
			randomizedChoices.push(option);
		} 
	}

	randomizedChoices = this.randomizeArray(randomizedChoices);

	for (var i = 1; i <= optionCount; i++) {
		var option = choiceList.options[i];
		var fixed = option.getAttribute("fixed");
		if (!(fixed && fixed == "fixed")) {
			if (randomizedChoices.length == 0) break;
			choiceList.options[i] = randomizedChoices.pop();
		}
	}

	this.SetChooseOneValue(heading, selectedValue);
};

/// <summary>
/// Randomizes the Choices in a Choose One Radio Buttons or Choose Many Check Boxes.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="numChoices">Total number of choices in the question</param>
VcSurvey.prototype.RandomizeListButtons = function(heading, numChoices) {
	var randomizedChoices = new Array();

	for (var i = 1; i <= numChoices; i++) {
		var spanChoice = document.getElementById(heading + "C" + i);
		if (spanChoice) {
			var fixed = spanChoice.getAttribute("fixed");
			if (!(fixed && fixed == "fixed")) {
				randomizedChoices.push(spanChoice);
			}
		}
	}

	randomizedChoices = this.randomizeArray(randomizedChoices);

	for (var i = 1; i <= numChoices; i++) {
		if (randomizedChoices.length == 0) break;
		var spanChoice = document.getElementById(heading + "C" + i);
		if (spanChoice) {
			var fixed = spanChoice.getAttribute("fixed");
			if (!(fixed && fixed == "fixed")) {
				spanChoice.swapNode(randomizedChoices.pop());
			}
		}
	}
};

/// <summary>
/// Randomizes the rows in a Matrix Question.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.RandomizeTableRows = function(heading, numChoices) {
	var table = document.getElementById("T" + heading);
	var tbodyElements = table.getElementsByTagName("tbody");
	var tbody = tbodyElements.item(0);
	var trElements = tbody.getElementsByTagName("tr");
	var randomizedRows = new Array();

	for (var i = 0; i < trElements.length; i++) {
		var tr = trElements.item(i);
		randomizedRows.push(tr);
	}

	randomizedRows = this.randomizeArray(randomizedRows);

	for (var i = 0; i < trElements.length; i++) {
		var tr = trElements.item(i);
		if (tr.swapNode) {
			tr.swapNode(randomizedRows[i]);
		} else {
			tr.innerHTML = randomizedRows[i].innerHTML;
		}
	}
};

/// <summary>
/// Private - Randomizes an array of items.
/// </summary>
/// <param name="heading">Array of items to randomize</param>
VcSurvey.prototype.randomizeArray = function(array) {
	for (var i = 0; i < array.length; i++) {
		var j = Math.floor(Math.random() * array.length);
		var item1 = array[i];
		var item2 = array[j];
		array[i] = item2;
		array[j] = item1;
	}
	return array;
};

/// <summary>
/// Jumps to the specified question on the current page and displays an optional alert message.
/// </summary>
/// <obsolete>
/// Used for pre Vovici v5 client-side validation. Maintained for backwards compatibility.
/// <obsolete>
/// <param name="heading">Anchor Heading to jump to e.g. A1 for Question Q1</param>
/// <param name="message">Alert message to display</param>
VcSurvey.prototype.GotoQuestion = function(heading, message) {
	window.location = "#" + heading;
	if (message) {
		alert(message);
	}
};

/// <summary>
/// Jumps to the first validation error on the page.
/// Returns true if success.
/// </summary>
VcSurvey.prototype.GotoFirstError = function() {
	if (this.firstValidationMessage) {
		var messageElement = document.getElementById(this.firstValidationMessage);
		var selectedPosX = 0;
		var selectedPosY = 0;
		while(messageElement != null){
			selectedPosX += messageElement.offsetLeft;
			selectedPosY += messageElement.offsetTop;
			messageElement = messageElement.offsetParent;
		}
		window.scrollTo(selectedPosX,selectedPosY);
		return true;
	}
	return false;
};

/// <summary>
/// Given a Choose One Dropdown or List Box question
/// Jumps to the anchor specified in the destination attribute for the selected choice.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
VcSurvey.prototype.SelectJump = function(heading) {
	// Get Selected Choice
	var choiceList = document.getElementById(heading);
	var selectedOption = choiceList.options[choiceList.selectedIndex];
	// Jump to destination if set
	var destination = selectedOption.getAttribute("destination");
	if (destination && destination != '') {
		window.location = "#" + destination;
	}
}

/// <summary>
/// Resets client-side validation.
/// </summary>
VcSurvey.prototype.ResetValidation = function(messageId) {
	this.firstValidationMessage = null;
	for (messageId in this.validationMessages) {
		var messageElement = document.getElementById(messageId);
		messageElement.style.display = "none";
	}
};

/// <summary>
/// Sets ghost text for the spcified fill in the blank question.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="text">Text to display when field is empty.</param>
VcSurvey.prototype.SetGhostText = function(heading, text) {
	var element = document.getElementById(heading);
	if (element) {
		element.setAttribute("ghostText", text);
		element.setAttribute("originalStyle", element.className);
		if (element.value == '') {
			element.value = text;
			element.className = "ghost-text";
			this.AddEvent(element, "focus", function(e){
				if(element.className == "ghost-text") {
					element.className = element.getAttribute("originalStyle");
					element.value = '';
				}
			});
			this.AddEvent(element, "blur", function(e){
				if (element.value == '') {
					element.className = "ghost-text";
					element.value = element.getAttribute("ghostText");
				}
			});
		}
	}
};

/// <summary>
/// Clear ghost text on all text boxes for submission.
/// </summary>
VcSurvey.prototype.ClearGhostText = function() {
	var form = survey.SurveyForm();
	var inputElements = form.getElementsByTagName("input");
	for (var i = 0; i < inputElements.length; i++) {
		var inputElement = inputElements.item(i);
		if (inputElement.type == "text") {
			var ghostText = inputElement.getAttribute("ghostText");
			if (ghostText && ghostText == inputElement.value) {
				inputElement.value = '';
			}
		}
	}
};

/// <summary>
/// Displays the specified validation message.
/// </summary>
/// <param name="messageId">Id of validation message to display</param>
/// <param name="placeHolder1">Optional value to replace %1 in validation message</param>
/// <param name="placeHolder2">Optional value to replace %2 in validation message</param>
VcSurvey.prototype.ShowValidationMessage = function(messageId, placeHolder1, placeHolder2) {
	var messageElement = document.getElementById(messageId);
	if (messageElement) {
		// We need to store the validation message to preserve placeholders
		if (!this.validationMessages[messageId]) {
			this.validationMessages[messageId] = messageElement.innerHTML;
		}
		// Get message text and replace place holders
		var messageText = this.validationMessages[messageId];
		if (placeHolder1) messageText = messageText.replace("%1", placeHolder1);
		if (placeHolder2) messageText = messageText.replace("%2", placeHolder2);
		messageElement.innerHTML = messageText;
		// Make Visible
		messageElement.style.display = "";
		if (!this.firstValidationMessage) this.firstValidationMessage = messageId;
	}
};

/// <summary>
/// Shows a Tool Tip for the specified glossary id relative to the calling word.
/// </summary>
/// <param name="heading">The element triggering the tool tip</param>
/// <param name="glossaryId">A valid glossary id</param>
VcSurvey.prototype.ShowToolTip = function(element, glossaryId) {
	var glossaryElement = document.getElementById(glossaryId);
	if (glossaryElement) {
		glossaryElement.style.visibility = "visible";
		// Set Top
		var top = 0;
		var topElement = element;
		while(topElement.offsetParent) {
			top += topElement.offsetTop;
			topElement = topElement.offsetParent;
		}
		top -= glossaryElement.offsetHeight
		if (top < 0) top = element.offsetTop + element.offsetHeight;
		glossaryElement.style.top = top + "px";
		// Set Left
		var width = document.body.offsetWidth;
		var left = 0;
		var leftElement = element;
		while(leftElement.offsetParent) {
			left += leftElement.offsetLeft;
			leftElement = leftElement.offsetParent;
		}
		if ((left + glossaryElement.offsetWidth) > width) {
			left = Math.max(0, width - glossaryElement.offsetWidth);
		}
		glossaryElement.style.left = left + "px";
	}
};

/// <summary>
/// Hides the Tool Tip with the specified glossary id for the calling word.
/// </summary>
/// <param name="heading">The element triggering the tool tip</param>
/// <param name="glossaryId">A valid glossary id</param>
VcSurvey.prototype.HideToolTip = function(element, glossaryId) {
	var glossaryElement = document.getElementById(glossaryId);
	if (glossaryElement) {
		glossaryElement.style.visibility = "hidden";
	}
};

/// <summary>
/// Displays the survey Jump Page.
/// </summary>
VcSurvey.prototype.ShowJumpPage = function() {
	var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
	var url = document.PdcSurvey.action + "?s=" + document.PdcSurvey.PdcSessionId.value + "&jump=1";
	if (!is_chrome && window.showModalDialog) {
		window.showModalDialog(url, document.PdcSurvey, "status:no;resizable:yes;center:yes;help:no;minimize:no;maximize:yes;scroll:yes;statusbar:no;");
	} else {
		window.open(url, document.PdcSurvey.PdcSessionId.value + "JUMP", "modal=yes, location=no, menubar=no, resizable=yes, scrollbars=yes, status=no, toolbar=no, dialog=yes");
	}
};

/// <summary>
/// Emails the current result page to the participants e-mail address on file.
/// </summary>
/// <param name="confirmation">Optional confirmation text to display after sending Ajax request.</param>
VcSurvey.prototype.EmailResults = function(confirmText) {
	var currentPage = document.getElementById("PdcCurrentPage");
	if (currentPage) {
		var args = "EMAILRESULTS;" + currentPage.value;
		this.AjaxRequest(args);
		if (confirmText) alert(confirmText);
	}
};

/// <summary>
/// Emails the current result page to the participant prompting for an e-mail address.
/// </summary>
/// <param name="promptText">Text to prompt for e-mail address.</param>
/// <param name="confirmText">Optional confirmation text to display after sending Ajax request.</param>
/// <param name="incorrectEmailText">Optional text to display if entered email address is incorrect.</param>
VcSurvey.prototype.EmailResultsPrompt = function(promptText, confirmText, incorrectEmailText) {
	var currentPage = document.getElementById("PdcCurrentPage");
	if (currentPage) {
		var email = prompt(promptText, '');
		if (email) {
			if (email.match(/^[\w_\-\.\']+[\%\+]?[\w_\-\']*\@[0-9a-zA-Z\-]+\.[0-9a-zA-Z\-\.]+$/)) {
				var args = "EMAILRESULTS;" + currentPage.value + ";" + email;
				this.AjaxRequest(args);
				if (confirmText) alert(confirmText);
			} else if (incorrectEmailText && incorrectEmailText != '') {
				alert(incorrectEmailText);
			}
		}
	}
};

/// <summary>
/// Populates the following list based on the
/// item selected in the specified list.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="index">Index of list who's value has changed</param>
VcSurvey.prototype.GetNextList = function(heading, index) {
	this.ClearSubsequentLists(heading, index);
	var listElement = document.getElementById(heading + "_" + index);
	var selectedItem = listElement.options[listElement.selectedIndex];
	var listId = selectedItem.getAttribute("listid");
	if (listId && listId != 0) {
		var args = "GETLIST;" + heading + ";" + index + ";" + listId;
		this.AjaxRequest(args);
	}
};

/// <summary>
/// Populates the first list based on the listid.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="index">Index of list who's value has changed</param>
VcSurvey.prototype.GetFirstList = function(heading, listId) {
	var args = "GETLIST;" + heading + ";" + 0 + ";" + listId;
	this.AjaxRequest(args);
};

/// <summary>
/// Populates the following list using a web service call.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="index">Index of list who's value has changed</param>
VcSurvey.prototype.GetNextListWS = function(heading, index) {
	// Get selected values of list items up to and including this list
	var listValues;
	for(var i = 1; i <= index; i++) {
		var nextListElement = document.getElementById(heading + "_" + i);
		if (nextListElement && nextListElement.tagName == "SELECT") {
			var selectedIndex = nextListElement.selectedIndex;
			if (selectedIndex >= 0) {
				var selectedValue = nextListElement.options[selectedIndex].value;
				listValues += ";" + heading + "_" + i + "=" + selectedValue;
			}
		}
	}
	this.ClearSubsequentLists(heading, index);
	var args = "GETLISTWS;" + heading + ";" + index + listValues;
	this.AjaxRequest(args);
};

/// <summary>
/// Removes all the choices in the list following the
/// specified index except for the list caption.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="index">Index of list after which choices should be removed</param>
VcSurvey.prototype.ClearSubsequentLists = function(heading, index) {
	var nextListIndex = parseInt(index) + 1;
	var nextListElement = document.getElementById(heading + "_" + nextListIndex);
	while (nextListElement && nextListElement.tagName == "SELECT") {
		var optionCount = nextListElement.options.length;
		for(var i = 0; i < optionCount; i++) {
			nextListElement.options[1] = null;
		}
		nextListElement.disabled = true;
		nextListIndex++;
		nextListElement = document.getElementById(heading + "_" + nextListIndex);
	}
};

/// <summary>
/// Populates the specified list from an XML collection of ListItems.
/// </summary>
/// <param name="heading">A valid dbHeading</param>
/// <param name="index">Index of list after to populate</param>
VcSurvey.prototype.PopulateList = function(heading, index, xml) {
	var listElement = document.getElementById(heading + "_" + index);
	var listItems = xml.documentElement;
	var optionCount = 1;
	for (i = 0; i < listItems.childNodes.length; i++) {
		var listItem = listItems.childNodes[i];
		if (listItem.nodeType == 1) {
			listElement.options[optionCount] = new Option(listItem.firstChild.firstChild.nodeValue, listItem.getAttribute('value'));
			listElement.options[optionCount].setAttribute("listid", listItem.getAttribute('targetlistid'));
			if (listItem.getAttribute("selected") == "true") {
				listElement.options[optionCount].setAttribute("selected", "selected");
				if(listElement.onchange) {
					listElement.onchange();
				}
			}
			optionCount++;
		}
	}
	listElement.disabled = false;
};

/// <summary>
/// Creates an instance of the XML HTTP Request object
/// Used for subsequent Ajax Requests.
/// </summary>
VcSurvey.prototype.GetXmlHTTP = function() {
	if (window.XMLHttpRequest) {
		return new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		return new ActiveXObject("Microsoft.XMLHTTP");
	}
};

/// <summary>
/// Submits an Ajax Request to the Survey Engine.
/// </summary>
/// <param name="parameters">Arguments to submit with Ajax Request</param>
VcSurvey.prototype.AjaxRequest = function(parameters) {
	var xmlhttp = this.GetXmlHTTP();
	if (xmlhttp != null) {
		xmlhttp.onreadystatechange = function() {
			if (xmlhttp.readyState == 4) {
				// Safari may return undefined
				if (xmlhttp.status == 200 || typeof(xmlhttp.status) == 'undefined') {
					var parts = parameters.split(';');
					var xml = null;
					if (window.ActiveXObject) {
						xml = new ActiveXObject("Microsoft.XMLDOM");
						xml.loadXML(xmlhttp.responseText);
					} else {
						xml = xmlhttp.responseXML;
					}
					switch(parts[0]) {
						case "GETLIST":
						case "GETLISTWS":
							survey.PopulateList(parts[1], parseInt(parts[2]) + 1, xml);
							break;
					}
				} else {
					alert("Ajax Request Failed: " + xmlhttp.status);
				}
			}
		}
		// Strip server path
		var url = this.SurveyForm().action;
		if(url.indexOf("/") != 0) {
			var pos = url.indexOf("//");
			pos = url.indexOf("/", pos + 2);
			url = url.substr(pos);
		}
		url = url.replace("se.ashx", "list.ashx");
		xmlhttp.open("GET", url, true);
		// Safari Caching Bug
		xmlhttp.setRequestHeader('If-Modified-Since','Wed, 15 Nov 1995 00:00:00 GMT');
		xmlhttp.setRequestHeader("isajaxrequest", "1");
		xmlhttp.setRequestHeader("ajaxargs", parameters);
		xmlhttp.setRequestHeader("PdcSessionId", this.GetTextValue("PdcSessionId"));
		xmlhttp.send('');
	}
};

/// <summary>
/// Registers an event for an element.
/// </summary>
/// <param name="element">Element on which to fire an event</param>
/// <param name="eventName">Name of event to fire</param>
/// <param name="eventFunction">Function to run on element event</param>
VcSurvey.prototype.AddEvent = function(element, eventName, eventFunction) {
	if (element.addEventListener) {
		element.addEventListener(eventName, eventFunction, false);
	} else if (element.attachEvent) {
		element.attachEvent("on" + eventName, eventFunction);
	} else {
		element["on" + eventName] = eventFunction;
	}
};

/// <summary>
/// Prevents form from submitting by pressing Enter key in a Fill In Question.
/// </summary>
VcSurvey.prototype.DisableEnterKey = function() {
	var form = survey.SurveyForm();
	if (form) {
		var inputElements = form.getElementsByTagName("input");
		for (var i = 0; i < inputElements.length; i++) {
			var inputElement = inputElements.item(i);
			// Attach OnKeyPress event to fill in's
			if (inputElement.type == "text" || inputElement.type == "password") {
				survey.AddEvent(inputElement, "keypress", function(e){
					// If use hits Enter
					if(e.keyCode==13) {
						// Cancel Form Submission that bypasses onsubmit event
						e.returnValue = false;
						e.cancel = true;
						// Required For FireFox
						if(e.preventDefault)e.preventDefault();
						// Submit the form, calling validation
						survey.AutoAdvance();
					}
				});
			}
		}
	}
};
