// alert("Module formsLib.js [v20021112.01] has loaded.");
//  <!-- to use this module, copy following (commented) line to your html file -->
//	<!-- <script src="/{server_alias}/includes/formsLib.js"></script> -->

/*
	name:	formsLib.js
	dir:	\htdocs\includes
	by: 	OpenAsia Solutions Pte Ltd
	func:	validates formfields based on data dictionary retrieved from database;
			forms that use this library must be parsed by Framer to load the dictionary;
			see dictionary index values (below) for structure of the data dictionary;
			on any error, the dictionary array is updated to flag the error status;
			on entry, expects dictionary indexes to be declared using 'setIndex()':
				dd_id_index, dd_entity_index, dd_formname_index, dd_formfield_name_index, 
				dd_field_name_index, dd_prompt_index, dd_required_index, dd_default_index,
				dd_char_index, dd_numeric_index, dd_validity_index, dd_email_index, 
				dd_date_index, dd_minlength_index, dd_maxlength_index, dd_default_index, 
				dd_error_index
			NOTE: requires 'commonLib.js' for winAlert messages, 'dateLib.js' for date validation
	rev: 	19990805, wmc, created this module
			19990818, wmc, added new functions 'checkLoginOk', 'isCleanInput',
				'isAlphaFirst', and 'isOneWord'; fixed checks at end of string
			19990822, wmc, added new function 'clearAll'
			19990823, tian, temporary disable the email checking function by returning true
							added a underscore in the isValidInput check
			19991121, wmc, added a new column in the dictionary for the 'form name'
			20000208, wmc, added suppport for 'preLoad' and 'getFieldList'
			20000217, wmc, added support for selection and checking of check box fields,
				modified handling for radio buttons
			20000928, wmc, added support for credit card test
			20001001, wmc, dropped explicit declaration of dictionary fieldnames, 
				preferring to dynamically load them from the column names in the dictionary
				table, and changed 'dict_<fieldname>_index' to 'dd_<fieldname>_index'
			20001008, wmc, added function 'trimCommas' to truncate lists with trailing commas;
				fixed 'getFieldList' so that all form fields are added to the list, not
				just those appearing in the dictionary; added supporting function 'isToken';
				fixed bugs in 'preLoad', 'checkboxSetState' and 'checkboxGetState'
			20010212, cj, added function checkNumericOk to check for all the numeric values in the form
				is ok.
			20010216, cj, added function checkCharOk to check for all the character values in the form
				is ok.
			20011001, wmc, modified form references to allow this script to be included in the
				top dataframe (ie- forms are assumed to be in the 'form_target_frame'); also 
				created 'setIndex' function to store dictionary indexes in this frame 
			20011011, wmc, added 'replace' function, and included replacement of apostrophe
				and quotes as a standard feature of formfield validation (in 'checkInput')
			20011125, wmc, added support for date validation using the functions in 'dateLib.js'
			20020428, wmc, cleaned up checkbox
			20020505, wmc, fixed bug in listSelect that caused it to fail, added textarea to 
				fields supporting 'isValidInput' function and modified error message to show
				invalid input chars, added function 'restoreChars'
			20020601, wmc, added check for null string in 'isCharInput' and similar functions,
				and added support for select lists to 'checkCharOk' and similar functions
			20020731, wmc, added function 'listSelectValue' to permit search on list values
			
	--------------------------------------------------------------------------------
	Following are the common client-side functions defined in this module.
	--------------------------------------------------------------------------------
	setIndex			sets a global variable (used to define dictionary indexes)
	preLoad				use array of database fields and values to preload form field values
	getFieldList		assemble fieldnames into 2 lists, separating numeric & text fields
	checkAll			validates all fields on the form as per data dictionary
	clearAll			clears all fields on the form
	checkDictionaryOk	validates the data dictionary itself
	checkInputOk		validates fields to confirm that no unexpected input is accepted
	restoreChars		restores characters from HTML format to form input display format
	checkRequiredOk		tests that all required fields are non-blank
	checkEmailsOk		tests that form fields with email addresses are valid
	checkDatesOK		tests that form fields with input in the SQL date format are valid
	checkCharOk			test that form fields with character values are valid
	checkNumericOk 		tests that form fields with numeric values are valid
	checkLengthsOk		tests that form field input fits required length (min & max)
	checkLoginOk		tests that form field input has first character alpha and is one word
	checkPasswordMatch	tests that form fields for password and confirmation match
	isCreditCard		tests if string is a valid credit card, performs Luhn mod-10 validation
	isEmail				tests that a string is valid as an email address
	isToken				tests if matchstr is present in the supplied tokenlist
	isValidInput		tests an individual string for unexpected input
	isCleanInput		tests an individual string for input without symbols
	isCharInput			tests an individual string for non-alpha input
	isNumericInput		tests an individual string for non-numeric input
	isEmpty				tests that a string is not null or empty
	isAlphaFirst		tests that a string has first character alpha
	isOneWord			tests that a string includes no whitespace characters
	radioSelect			match a string with one option in a radio button group
	radioGetValue 		gets value of selected radio button in group
	isRadioSelected		tests whether a radio button is selected (one only)
	radioGetIndex		gets position of selected radio button in group
	isRadio				tests whether a field is a valid radio button group
	checkboxGetValues	gets the values associated with a set of checkboxes
	isCheckboxSelected	tests whether one or more elements of a checkbox group is selected
	checkboxSetState	sets the state of a checkbox ('on' or 'off')
	checkboxGetState	gets the state of a checkbox ('on' or 'off') into state variable
	listSelect			match a string with one text option in dropdown list	
	listSelectValue		match a string with one value option in dropdown list	
	rePaint				sets color of prompts to red for each field with error
	printItemList		diagnostic for checking data structure
	printform			diagnostic for checking contents of the form
	cleanup				support for isCreditCard, removes delimiters such as hyphen ("-")
	trimCommas			truncates commas from end of string list
	replace				performs simple search and replace on any string
	--------------------------------------------------------------------------------
*/

var formsLib = this;

var global = top.dataframe;						// needed for window popup 'winAlert'
var form_target_frame = "top.bodyframe";		// global target for all 'form' references
var bad_chars = "";

function setIndex (name, value) {
	eval(name + "='" + value + "'");
}

function preLoad (form, dictionary) {
		// index parameters are optional, required only if called from another frame
	var err = (!dictionary);
	if (!err) {
		for (var i = 0; i < dictionary.length; i++) {
			var j = 0;			// find the formfield which corresponds
			while ((j < form.length) && form.elements[j] &&
			 (form.elements[j].name != dictionary[i][dd_formfield_name_index])) j++;
			if (j < form.length) {
					// radio field requires evaluating absolute object reference
				formField = eval(form_target_frame + ".document." + form.name + "." +
				 form.elements[j].name);
				if (formField.type == "text" || formField.type == "password" || formField.type == "textarea") 
					formField.value = dictionary[i][dd_default_index];
				if (formField.type == "select-one") 
					listSelect(formField, dictionary[i][dd_default_index]);
				if (formField.type == "checkbox") 
					checkboxSetState (formField, dictionary[i][dd_default_index]);
				if (formField[0] && formField[0].type == "radio") 
					radioSelect(formField, dictionary[i][dd_default_index]);
			} else {
				/*
				alert("The field '" + dictionary[i][dd_formfield_name_index] +
					"', indicated in the data dictionary, does not appear in the '" +
					form.name + "' form.");
				*/
				continue;	// leave this null statement in if not presenting an alert
			}
		}
	} else {
		global.commonLib.winAlert("The dictionary cannot be found.  Check for javascript errors.");
	}
}

function getFieldList (form, dictionary, stoplist) {
		// Function assembles field list which includes each field in the dictionary, 
		// along with any other fields on the form (and assumes these are textual input);
		// discriminates between numeric and text fields if indicated in the dictionary,
		// and uses the override operater (ie- "!" as in "db_field!form_field") to indicate
		// that the database fieldname may be different than the form fieldname.
		// NOTE: duplicate fieldnames (such as those on radio buttons), or those appearing
		// in the stoplist, are ignored.  Fields manually entered into the hidden fieldlists
		// are included (not other hidden fields), and these override the dictionary definitions.
		// This function may be called with or without a stoplist.
		
		// Note - function could be improved to ensure that all dictionary fields are picked
		// up if they exist on the page (eg- one in a hidden input).
		
	var supported_fieldtypes = "text, textarea, password, radio, select-one";
	var err = (!dictionary || !form.fields_text || !form.fields_numeric);
	if (!err) {
		var list_t = "";
		var list_n = "";
		// form.reset();		// ensure fields not duplicated if reloaded using 'browser back'
		// temporarily disabled because:
		// (1) in Netscape, the reset causes default data in fields to be removed
		// (2) in both browsers, the reset causes checkbox, option list settings to be lost
		if (form.fields_text.value != '') list_t = trimCommas(form.fields_text.value) + ", ";
		if (form.fields_numeric.value != '') list_n = trimCommas(form.fields_numeric.value) + ", ";
		var found_fieldnames = list_t + list_n;
		for (var i = 1; i < form.length; i++) {
			if (isToken(supported_fieldtypes, form.elements[i].type) &&
			 !isToken(found_fieldnames, form.elements[i].name) && 
			 (arguments.length < 3 || !isToken(stoplist, form.elements[i].name))) {
				found_fieldnames += form.elements[i].name + ", ";
				var j = 0;			// find the database field which corresponds
				while ((j < dictionary.length) &&
				 (dictionary[j][dd_formfield_name_index] != form.elements[i].name)) j++;
		 		if (j < dictionary.length) {
					if (dictionary[j][dd_numeric_index] == true) {
						list_n += dictionary[j][dd_field_name_index] + "!" +
						 dictionary[j][dd_formfield_name_index] + ", ";
					} else {
						list_t += dictionary[j][dd_field_name_index] + "!" +
						 dictionary[j][dd_formfield_name_index] + ", ";
					}
				} else {			// if not in dictionary, treat field as a textual input
					list_t += form.elements[i].name + ", ";
				}
			}
		}
		form.fields_text.value = trimCommas(list_t);
		form.fields_numeric.value = trimCommas(list_n);
		/*
		alert(	"got text fields: \t" + form.fields_text.value + "\n\n" +
				"and numeric fields: \t" + form.fields_numeric.value + "\n" +
				"\n");
		*/
	} else {
		global.commonLib.winAlert ("Can't find 'fields_text' or 'fields_numeric' input field(s) in the '" +
			form.name + "' form, or the dictionary is invalid.");
	}
}

function checkAll (form, dictionary) {
	var err = (!checkDictionaryOk (form, dictionary));
	if (!err) err = (!checkInputOk (form, dictionary));
	if (!err) err = (!checkRequiredOk (form, dictionary));
	if (!err) err = (!checkLengthsOk (form, dictionary));
	if (form.login)
		if (!err) err = (!checkLoginOk (form, dictionary, form.login.name));
	if (form.password)
		if (!err) err = (!checkLoginOk (form, dictionary, form.password.name));
	if (form.password && form.password_confirm)
		if (!err) err = (!checkPasswordMatch (form, dictionary, form.password.name, form.password_confirm.name));
	if (!err) err = (!checkEmailsOk (form, dictionary));
	return (!err);
}

function clearAll (form) {
		// this function is intended to clear fields that have been present using MediaWare
		// variables, and is invoked together with a form reset, eg-
		// <!-- input type=reset value=" Reset " onClick="clearAll(this.form);" -->
	for (var i = 1; i < form.length; i++) {
		if (form.elements[i].type == "text" || form.elements[i].type == "password") 
			form.elements[i].value = '';
	}
	// alert("All fields have been cleared.");
}

function checkDictionaryOk (form, dictionary) {
	var str = "";				// string to store error message
	for (var i = 0; i < dictionary.length; i++) {
		dictionary[i][dd_error_index] = false;	// reset error status
		if (dictionary[i][dd_formfield_name_index] == 'null') {
			str = "The name of a field in the data dictionary is null or empty!";
			dictionary[i][dd_error_index] = true;
			break;
		}
		if (!eval(form_target_frame + ".document." + form.name + "." +
		  dictionary[i][dd_formfield_name_index])) {
			/*
			str = "Field '" + dictionary[i][dd_formfield_name_index] +
				"' does not appear in the form '" + form.name + "'.";
			dictionary[i][dd_error_index] = true;
			break;
			*/
			continue;	// leave this null statement in if not presenting an alert
		}
	}
	if (str != "")
		global.commonLib.winAlert ("The dictionary cannot be used for validation: \n" + str);
	return (str == "");
}

function checkInputOk (form, dictionary) {
	var supported_fieldtypes = "text, textarea, password";
	var str = "";				// string to store error message
	var flag="";	

	for (var i = 1; i < form.length; i++) {
		if (isToken(supported_fieldtypes, form.elements[i].type)) {
				// convert apostrophe, quotes and endline to HTML style input
			form.elements[i].value = replace(form.elements[i].value, "'", "&#39;");
			form.elements[i].value = replace(form.elements[i].value, '"', '&#34;');
			form.elements[i].value = replace(form.elements[i].value, '\r\n', '<br>');

			if (!isValidInput(form.elements[i].value)) {
				str += "\t" + form.elements[i].name + " (ie- '" + this.bad_chars + "')\n";
				var j = 0;		// update dictionary to indicate error in this field
				while ((j < dictionary.length) &&
				 (dictionary[j][dd_formfield_name_index] != form.elements[i].name)) j++;
		 		if (j < dictionary.length) 
					dictionary[j][dd_error_index] = true;
			}
		}
	}
	if (str != "")
		global.commonLib.winAlert ("Following fields contain invalid input: \n" + str);
	return (str == "");
}

function restoreChars (form, dictionary) {
	var supported_fieldtypes = "text, textarea, password";
	for (var i = 1; i < form.length; i++) {
		if (isToken(supported_fieldtypes, form.elements[i].type)) {
				// convert HTML style input to standard apostrophe, quotes and endline
			form.elements[i].value = replace(form.elements[i].value, "&#39;", "'");
			form.elements[i].value = replace(form.elements[i].value, '&#34;', '"');
			form.elements[i].value = replace(form.elements[i].value, '<br>', '\r\n');
		}
	}
}

		// ensure that the required fields are not blank
function checkRequiredOk (form, dictionary) {
	var str = "";				// string to store error message
	for (var i = 0; i < dictionary.length; i++) {
		formField = eval(form_target_frame + ".document." + form.name + "." +
			 dictionary[i][dd_formfield_name_index]);
		if (dictionary[i][dd_required_index] == 1) {
			var j = 0;			// find the formfield which corresponds
			while ((j < form.length) && form.elements[j] &&
			 (dictionary[i][dd_formfield_name_index] != form.elements[j].name)) j++;
			
			if (j < form.length) { 
				if (form.elements[j].type == "text" || form.elements[j].type == "password" || form.elements[j].type == "textarea") {
					if (formField.value == "") {
						str += "\t" + dictionary[i][dd_prompt_index] + "\n";
						dictionary[i][dd_error_index] = true;
					}
				}
		// GUL added this code on 20001018 to check the select list
		// This code is applicable only if the first option in
		//   drop down list is empty.
				if (form.elements[j].type == "select-one") {
					for(sel=0;sel<formField.length;sel++) {
					if (formField.options[sel].selected == true && formField.options[sel].value.length == 0) {
						str += "\t" + dictionary[i][dd_prompt_index] + "\n";
						dictionary[i][dd_error_index] = true;
					}
				   }	
				}
		// GUL's Code ends here

				if (form.elements[j].type == "radio") {
					// WAS: if (!isRadioSelected(eval("form." + form.elements[j].name))) {
					if (!isRadioSelected(eval(formField))) {
						str += "\t" + dictionary[i][dd_prompt_index] + "\n";
						dictionary[i][dd_error_index] = true;
					}
				}
			} 
		}
	}
	if (str != "")
		global.commonLib.winAlert ("Following fields are required, and cannot be left blank: \n" + str);
	return (str == "");
}

function checkEmailsOk (form, dictionary) {
	var str = "";				// string to store error message
	for (var i = 0; i < dictionary.length; i++) {
		if (dictionary[i][dd_email_index] == 1) {
			var j = 0;			// find the formfield which corresponds
			while ((j < form.length) && form.elements[j] &&
			 (dictionary[i][dd_formfield_name_index] != form.elements[j].name)) j++;
	 		if (j < form.length) { 
				if (!isEmail (form.elements[j].value)) {
					str += "\t" + dictionary[i][dd_prompt_index] + "\n";
					dictionary[i][dd_error_index] = true;
				}
			}
		}
	}
	if (str != "")
		global.commonLib.winAlert ("Following fields are not in the correct format " +
			"for email addresses (eg- 'abel@best.com'): \n" + str);
	return (str == "");
}

function checkDatesOk (form, dictionary) {
	if (!this.isValidDateString) {
		global.commonLib.winAlert("The date input cannot be checked because the library 'dateLib.js' " +
			"has not been loaded.");
	} else {
		var str = "";					// string to store error message
		var pattern = "yyyy-mm-dd";		// default format for date input
		for (var i = 0; i < dictionary.length; i++) {
			if (dictionary[i][dd_date_index] == 1) {
				var j = 0;			// find the formfield which corresponds
				while ((j < form.length) && form.elements[j] &&
				 (dictionary[i][dd_formfield_name_index] != form.elements[j].name)) j++;
		 		if (j < form.length) { 
					if (!isValidDateString (form.elements[j].value, pattern)) {
						str += "\t" + dictionary[i][dd_prompt_index] + "\n";
						dictionary[i][dd_error_index] = true;
					}
				}
			}
		}
		if (str != "")
			global.commonLib.winAlert ("Following fields are not in the correct format " +
				"for date input (eg- 'yyyy-mm-dd'), or contain invalid dates: \n" + str);
		return (str == "");
	}
}

function checkCharOk (form, dictionary) {
	var str = "";				// string to store error message
	for (var i = 0; i < dictionary.length; i++) {
		if (dictionary[i][dd_char_index] == 1) {
			var j = 0;			// find the formfield which corresponds
			while ((j < form.length) && form.elements[j] &&
			 (dictionary[i][dd_formfield_name_index] != form.elements[j].name)) j++;
	 		if (j < form.length) { 
				var value = form.elements[j].value;
				if (form.elements[j].type == "select-one")
					value = form.elements[j][form.elements[j].selectedIndex].value; 
				if (!isCharInput (value)) {
					str += "\t" + dictionary[i][dd_prompt_index] + "\n";
					dictionary[i][dd_error_index] = true;
				}
			}
		}
	}
	if (str != "")
		global.commonLib.winAlert ("Following fields are not in the correct format\n" +str);
	return (str == "");
}

function checkNumericOk (form, dictionary) {
	var str = "";				// string to store error message
	for (var i = 0; i < dictionary.length; i++) {
		if (dictionary[i][dd_numeric_index] == 1) {
			var j = 0;			// find the formfield which corresponds
			while ((j < form.length) && form.elements[j] &&
			 (dictionary[i][dd_formfield_name_index] != form.elements[j].name)) j++;
	 		if (j < form.length) { 
				var value = form.elements[j].value;
				if (form.elements[j].type == "select-one")
					value = form.elements[j][form.elements[j].selectedIndex].value; 
				if (!isNumericInput (value)) {
					str += "\t" + dictionary[i][dd_prompt_index] + "\n";
					dictionary[i][dd_error_index] = true;
				}
			}
		}
	}
	if (str != "")
		global.commonLib.winAlert ("Following fields are not in the correct format\n" +str);
	return (str == "");
}

function checkLengthsOk (form, dictionary) {
	var str = "";				// string to store error message
	for (var i = 0; i < dictionary.length; i++) {
		if ((dictionary[i][dd_minlength_index] != 0) || (dictionary[i][dd_maxlength_index] != 0)) {
			var j = 0;			// find the formfield which corresponds
			while ((j < form.length) && form.elements[j] &&
			 (dictionary[i][dd_formfield_name_index] != form.elements[j].name)) j++;

			/*/ diagnostic
			alert("lengths (actual,min,max): " + form.elements[j].name + ", " +
			form.elements[j].value.length + ", " + dictionary[i][dd_minlength_index] + ", " +
			dictionary[i][dd_maxlength_index]);
			*/
			
	 		if (j < form.length) { 
				if (dictionary[i][dd_minlength_index] != 0 && form.elements[j].value) {
					if ((form.elements[j].value.length < dictionary[i][dd_minlength_index])) {
						str += "\t" + dictionary[i][dd_prompt_index] + "\n";
						dictionary[i][dd_error_index] = true;
					}
				}
				if (dictionary[i][dd_maxlength_index] != 0 && form.elements[j].value) {
					if ((form.elements[j].value.length > dictionary[i][dd_maxlength_index])) {
						str += "\t" + dictionary[i][dd_prompt_index] + "\n";
						dictionary[i][dd_error_index] = true;
					}
				}
			}
		}
	}
	if (str != "")
		global.commonLib.winAlert ("Following fields exceed the minimum or maximum number of characters: \n" + str);
	return (str == "");
}

		// tests that form field input has first character alpha and is one word
function checkLoginOk(form, dictionary, fieldname) {
	formField = eval(form_target_frame + ".document." + form.name + "." + fieldname);
	var clean = isCleanInput(formField.value);
	var alphafirst = isAlphaFirst(formField.value);
	var oneword = isOneWord(formField.value);
	if (!clean || !alphafirst || !oneword) {
		if (!clean)
			global.commonLib.winAlert ("The field '" + fieldname + "' must not contain symbols: '" +
				formField.value + "'\n");
		if (!alphafirst)
			global.commonLib.winAlert ("The field '" + fieldname + "' must begin with a letter (A-Z): '" +
				formField.value + "'\n");
		if (!oneword)
			global.commonLib.winAlert ("The field '" + fieldname + "' must not include spaces: '" +
				formField.value + "'\n");
		var j = 0;			// find the formfield which corresponds
		while ((j < dictionary.length) &&
		 (dictionary[j][dd_formfield_name_index] != formField.name)) j++;
 		if (j < dictionary.length)
			dictionary[j][dd_error_index] = true;
	}
	return (clean && alphafirst && oneword);
}

function checkPasswordMatch (form, dictionary, fieldname1, fieldname2) {
	var str = "";				// string to store error message
	formField1 = eval(form_target_frame + ".document." + form.name + "." + fieldname1);
	formField2 = eval(form_target_frame + ".document." + form.name + "." + fieldname2);
	if (formField1.value != formField2.value)
  		str = "Password and confirmation do not match.\n";
  	if (str != "") {
		global.commonLib.winAlert (str);
		var j = 0;			// find the formfield which corresponds
		while ((j < dictionary.length) &&
		 (dictionary[j][dd_formfield_name_index] != formField1.name)) j++;
 		if (j < dictionary.length)
			dictionary[j][dd_error_index] = true;
		var j = 0;			// find the formfield which corresponds
		while ((j < dictionary.length) &&
		 (dictionary[j][dd_formfield_name_index] != formField2.name)) j++;
 		if (j < dictionary.length)
			dictionary[j][dd_error_index] = true;
	}
	return (str == "");
}

function isCreditCard (s) {
	// checks if the credit card number passes the Luhn mod-10 test.
	// check only works on cards with 13 - 19 digits

	if (s == null) return false;
	str = cleanup(s);
	if (str.length < 13 || str.length > 19 || !isNumericInput(str))
		return (false);
 
 	var sum = 0;
	var mul = 1;
	var len = str.length;
	var digit;
	var sum;
	var tpproduct;
	for (var i = 0; i < len; i++) {
		digit = str.substring(len-i-1, len-i);
		tproduct = parseInt(digit ,10) * mul;
		if (tproduct >= 10)
			sum += (tproduct % 10) + 1;
		else
			sum += tproduct;
		if (mul == 1)
			mul++;
		else
			mul--;
	}
	return ((sum % 10) == 0);  
}

function isEmail (s) {
	if (s == null) return false;
	str = s.toUpperCase(s);
	var alphanumerics = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	var delimiters = ".@-_";
	var i = 0;
	var c;
	var alpha = true;
	var delim = true;
	var at = str.indexOf("@");
	var dot = str.indexOf(".", at + 1);
	while ((i < str.length) && (alpha || delim)) {
		c = str.charAt(i);
		alpha = (alphanumerics.indexOf(c) != -1);
		if (!alpha) delim = (delimiters.indexOf(c) != -1);
		if (alpha || delim) i++;
	}
	return ((i == str.length) && (at > 0) && (dot < str.length - 2));
}

function isToken (tokenlist, matchstr) {
		// searches tokenlist to see if 'matchstring' matches any token in the list
		// a token start must be the first letter of the tokenlist, or follow a valid delimiter
		// a token end must be the last letter of the tokenlist, or be followed by a valid delimiter
	var found_token = false;
	if (!isEmpty(tokenlist) && !isEmpty(matchstr) && isCleanInput(matchstr) && isOneWord(matchstr)) {
		var delimiters = ";, ";
		var i = 0;
		var j = 0;
		var c;
		while (i >= 0 && i < matchstr.length && !found_token) {
			i = tokenlist.indexOf(matchstr, i);
			if (i != -1) {
				if (i > 0) c = tokenlist.charAt(i - 1);
				if (i == 0 || delimiters.indexOf(c) != -1) {
					j = i + matchstr.length;
					if (j < tokenlist.length) c = tokenlist.charAt(j);
					found_token = (j == tokenlist.length || delimiters.indexOf(c) != -1);
				}
				if (!found_token) i++;
			}
		}
	}
	return (found_token);		
}

function isValidInput (s) {
	if (s == null) return false;
	this.bad_chars = "";
	var str = s.toUpperCase(s);
	var alphanumerics = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	var delimiters = "@!?()+-.#\\/:;,<>[]*&_ ";
    if (isEmpty(str)) {
		return true;	// it's ok that fields be empty when checking input chars
	} else {
		var i = 0;
		var c;
		var alpha = true;
		var delim = true;
		while ((i < str.length) && (alpha || delim)) {
			c = str.charAt(i);
			alpha = (alphanumerics.indexOf(c) != -1);
			if (!alpha) delim = (delimiters.indexOf(c) != -1);
			if (alpha || delim) 
				i++;
			else 
				this.bad_chars += str.charAt(i);
		}
		return (i == str.length);		
    }
}

		// tests an individual string for input without symbols
function isCleanInput (s) {
	if (s == null) return false;
	var str = s.toUpperCase(s);
	var alphanumerics = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	var delimiters = "-_ ";
    if (isEmpty(str)) {
		return true;	// it's ok that fields be empty when checking input chars
	} else {
		var i = 0;
		var c;
		var alpha = true;
		var delim = true;
		while ((i < str.length) && (alpha || delim)) {
			c = str.charAt(i);
			alpha = (alphanumerics.indexOf(c) != -1);
			if (!alpha) delim = (delimiters.indexOf(c) != -1);
			if (alpha || delim) i++;
		}
		return (i == str.length);		
    }
}

function isCharInput (s) {
	if (s == null) return false;
	var str = s.toUpperCase(s);
	var alphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	var delimiters = "@()+-.#\\/':;,<>[]*& ";
    if (isEmpty(str)) {
		return true;	// it's ok that fields be empty when checking input chars
	} else {
		var i = 0;
		var c;
		var alpha = true;
		var delim = true;
		while ((i < str.length) && (alpha || delim)) {
			c = str.charAt(i);
			alpha = (alphas.indexOf(c) != -1);
			if (!alpha) delim = (delimiters.indexOf(c) != -1);
			if (alpha || delim) i++;
		}
		return (i == str.length);		
    }
}

function isNumericInput (s) {
	if (s == null) return false;
	var str = s;
	var numerics = "0123456789";
	var delimiters = "+-. ";		// doesn't check if math symbols are leading chars!
    if (isEmpty(str)) {
		return true;	// it's ok that fields be empty when checking input chars
	} else {
		var i = 0;
		var c;
		var number = true;
		var delim = true;
		while ((i < str.length) && (number || delim)) {
			c = str.charAt(i);
			number = (numerics.indexOf(c) != -1);
			if (!number) delim = (delimiters.indexOf(c) != -1);
			if (number || delim) i++;
		}
		return (i == str.length);		
    }
}

function isEmpty (s) {
	var i = 0;			// first skip leading spaces
	while (s != '' && s.charAt(i) == ' ') i++;
	return (s == null || i == s.length);
}

		// tests that a string has first character alpha
function isAlphaFirst(s) {
	var alphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	var c = s.toUpperCase().charAt(0);		// get the first character
	return (alphas.indexOf(c) != -1);
}

		// tests that a string includes no whitespace characters
function isOneWord (s) {
	var delimiters = " ";
	var i = 0;
	var c;
	var delim = false;
	while ((i < s.length) && (!delim)) {
		c = s.charAt(i);
		delim = (delimiters.indexOf(c) != -1);
		if (!delim) i++;
	}
	return (i == s.length);		
}

function radioSelect (buttongroup_obj, matchstr) {
	if (isRadio (buttongroup_obj) && matchstr != "") {
		for (var i = 0; i < buttongroup_obj.length; i ++) 
			buttongroup_obj[i].checked = (buttongroup_obj[i].value == matchstr);
		if (!isRadioSelected (buttongroup_obj)) {
			global.commonLib.winAlert("Error: '" + matchstr + "' is not one of the options in the '" +
				buttongroup_obj[0].name + "' radio button group!");	
		}
	}
}

function isRadioSelected (buttongroup_obj) {
	return (radioGetIndex(buttongroup_obj) != -1);		
}

function radioGetValue (buttongroup_obj) {
	var selection = "";
	var index = radioGetIndex (buttongroup_obj);
	if (index != -1) selection = buttongroup_obj[index].value;
	return (selection);
}

function radioGetIndex (buttongroup_obj) {
	var index = -1;
	if (isRadio (buttongroup_obj)) {
		var i = 0;
		while (i < buttongroup_obj.length && !buttongroup_obj[i].checked) i++;
		if (i < buttongroup_obj.length) index = i;
	}
	return (index);
}

function isRadio (buttongroup_obj) {
	var status = false;
	if (buttongroup_obj) {
		if (buttongroup_obj[0] && (buttongroup_obj[0].type == "radio")) 
			status = true;
		else
			global.commonLib.winAlert("Error: '" + buttongroup_obj.name + "' is not a valid radio group!");
	} else  {
		global.commonLib.winAlert("Error: 'isRadio' called without specifying a valid radio group!");
	}
	return (status);
}

function checkboxSetState (field_obj, state) {
	if (field_obj) 
		field_obj.checked = ((state == true || state == 'on' || state == '1') ? true : false);
	else
		global.commonLib.winAlert("Error: '" + field_obj.name + "' is not a valid checkbox!");
}

		// gets the values from a series of checkboxes into a list
		// use 'var value_list = checkboxGetValues (form, "a, b, g");'
function checkboxGetValues (form, field_list) {
	var values_list = "";
	var stopList = " ,";
	var fieldname;
	i = 0;
	j = 0;
	while (j < field_list.length) {
		j = field_list.indexOf(',', i);
		if (j < 0) j = field_list.length;
		formField = eval(form_target_frame + ".document." + form.name + "." + 
		  field_list.substr(i, j - i));
		if (formField.value != "") values_list += formField.value + ", ";
		while (++j < field_list.length && stopList.indexOf(field_list.charAt(j)) > 0);
		i = j;
	}
	return (trimCommas(values_list));
}

function isCheckboxInListSelected (form, fieldname_list) {
	var status = false;
	for (var item in fieldname_list.split(","))
		status = isCheckboxSelected(form, item);
	return (status);
}

	//check whether any named checkbox has been selected (omit name to validate all checkboxes)
function isCheckBoxSelected (form, fieldname) {
	var status = false;
	for (var i in form.elements) {
		if (form.elements[i].type == "checkbox") 
			 status = (form.elements[i].checked == true && 
			   (fieldname == null || form.elements[i].name == fieldname));
	}
	return (status);
}

	// gets the state of a checkbox (regardless of value) into a state variable
function checkboxGetState (field_obj) {
	var status = false;
	if (field_obj) status = (field_obj.checked ? true : false);
	return (status);
}

	// All checkboxes gets state toggled
function checkBox_selectAll(form, obj) {
	var counter=0;
	if (obj.checked == true){
		for(i=0;i<form.elements.length;i++) {
			if(form.elements[i].type == "checkbox" ) form.elements[i].checked = true;
		}
	} else {
		for(i=0;i<form.elements.length;i++){
				if(form.elements[i].type == "checkbox" ) form.elements[i].checked = false;
		}
	}
}

	// try to match a string to text options in a form drop-down list
function listSelect (list, matchstr) {
	var i = 0;
	while (i < list.length && list[i].text.toLowerCase() != matchstr.toLowerCase()) i++;
	if (i < list.length)
		list.selectedIndex = i;		// set the selection pointer into list
	else
		i = list.selectedIndex;		// don't set the selection pointer, get default
	return i;
}

	// try to match a string to value options in a form drop-down list
function listSelectValue (list, matchstr) {
	var i = 0;
	while (i < list.length && list[i].value.toLowerCase() != matchstr.toLowerCase()) i++;
	if (i < list.length)
		list.selectedIndex = i;		// set the selection pointer into list
	else
		i = list.selectedIndex;		// don't set the selection pointer, get default
	return i;
}

	// get value of any item in option list, or (if index not specified) of the selected item
function listGetValue (list, index) {
	var str = "";
	if (list != null) {											// if list is not an object
		if (index == null) index = list.selectedIndex;			// if index not supplied
		if (index >= 0 && index < list.length) {				// if supplied index out of range
			if (list[index].value) {
				str = list[index].value;
			} else {
				alert("Error: listGetValue called with one or more values undefined.");
			}
		} else {
			alert("Error: listGetValue called with invalid index parameter.");
		}
	} else {
		alert("Error: listGetValue called without a valid list object.");
	}
	return (str);
}

function rePaint (form, dictionary) {
	var ERRCOLOR = "red";
	for (var i = 0; i < dictionary.length; i++) {
		if (dictionary[i][dd_error_index] == 1) {
			var j = 0;			// find the formfield which corresponds
			while ((j < form.length) &&
			 (dictionary[i][dd_formfield_name_index] != form.elements[j].name)) j++;
			if (!eval(form_target_frame + ".document." + form.name + "." + 
			  form.elements[j].name + "_color")) {
				global.commonLib.winAlert ("Color attribute field not defined: " + form.elements[j].name);
			} else {
				var formField = eval(form_target_frame + ".document." + form.name + "." + 
				  form.elements[j].name + "_color");
				formField.value = ERRCOLOR;
			}
		}
	}
}

		// diagnostic for checking data structure
function printItemList (list) {
	var str = "Item List Array:\n";
	for (var i = 0; i < list.length; i++) {
		str += "Row " + (i+1) + "\t";
		for (var j = 0; j < (list[i]).length; j++)
			str += list[i][j] + "\t";
		str += "\n";
	}
	global.commonLib.winAlert (str);
}

		// diagnostic for checking contents of the form
function printFormFields (form) {
	var str = "Contents of form Fields:\n";
	for (var i = 0; i < form.length; i++) {
		if (form.elements[i].type == "text" || form.elements[i].type == "password")
			str += form.elements[i].name + " = '" + form.elements[i].value + "'\n";
		if (form.elements[i].type.indexOf("select") >= 0) {
			str += form.elements[i].name + " = '" +
			form.elements[i][form.elements[i].selectedIndex].text + "'\n";
		}
		if (form.elements[i].type == "radio" && form.elements[i].checked) 
			str += form.elements[i].name + " = '" + form.elements[i].value + "'\n";
		if (form.elements[i].type == "checkbox") 
			str += form.elements[i].name + " = '" + form.elements[i].checked + "'\n";
	}
	global.commonLib.winAlert (str);
}

function cleanup (str) {
		// remove all the delimiters
	var validDelims = " -.\\/";
	for (var i = 0; i < str.length; i++) {
		var ch =  str.charAt (i);
		if (validDelims.indexOf (ch) != -1) 
			str = str.substr(0,i) + str.substr(i+1);
	}
	return (str);
}

	// remove trailing spaces and commas
function trimCommas (str) {
	var trimChars = ", ";
	var i = str.length - 1;
	while (i >= 0 && trimChars.indexOf(str.charAt(i)) != -1) i--;
	return (str.substr(0, i + 1));
}

function replace (str, from, to) {
	var ptr = str.indexOf(from);
	while (ptr != -1) {
		str = str.substr(0, ptr) + to + str.substr(ptr + from.length);
		ptr = str.indexOf(from);
	}
	return(str);
}
