/* $Revision$ */

var USGN, calendar, f_callback_data, _loc, url_prefix,
	re_amp, re_eq, commify_regex1, commify_regex2, refresh_files; //,
	// these are from external modules
	//df, $, oWin, Calendar, rs_get_data, encrypt, getCookie, unescape;

USGN = (function() {
	return {
		util    : {},
		storage : {},
		excel   : {}
	};
}());

// the following are stubs for 'hidden' functions or variables encased in
// closures. this is mainly to assist jslint
function date_today() {}
function new_Option() {}
function new_Options() {}
function signature_check() {}
function QueryString() {}
function CGI() {}

_loc = window.location;
url_prefix = _loc.href.match(/\/\/[^\/]*(\/[^\/]*\/)/) ? RegExp.$1 : undefined;

function cleanNum(n) {
	var a = String(n).replace(/,/g, '');
	a = Number(a.match(/([\-+]?(?:\d+(?:\.\d*)?|\.\d+))/) ? RegExp.$1 : '');
	return a;
}
// backwards compatibility
window._x = cleanNum;

function hid(obj) { return obj.type.toUpperCase() === 'HIDDEN'; }

re_amp = new RegExp('&', 'g');
re_eq  = new RegExp('=', 'g');
function ue(x) { return x.replace(re_amp, '%26').replace(re_eq, '%3D'); }

function evil(x) { return eval(x); }

commify_regex1 = /\d+\d{3}/;
commify_regex2 = /(\d+)(\d{3})/g;
function commify(num) {
	var a = String(num).split('.'), b = a[1], x_out = 0;
	a = a[0];

	while (commify_regex1.test(a)) {
		a = a.replace(commify_regex2, '$1,$2');
		x_out++;
		if (x_out > 5) {
			USGN.util.log('commify: error, too many commas');
			break;
		}
	}
	return b ? a + '.' + b : a;
}

function dollicommify(val) {
	var tmp = val;
	if (tmp === null || tmp === '') {
		tmp = 0;
	}
	return ('$' + commify(cleanNum(tmp).toFixed(2)));
}

function dateNum(d) { return Math.floor((new Date(d)).getTime()/86400000); }
// backwards compatibility
window._d = dateNum;

function dateNow(n) {
	var a = new Date();
	return dateNum(a.getTime() - (-(n||0) * 86400000) - (a.getTimezoneOffset() * 60 * 1000));
}

function numDate(n) {
	var     a = new Date((Number(n) + 1) * 1000 * 60 * 60 * 24),
		m = a.getMonth() + 1,
		d = a.getDate();

	if (m < 10) {
		m = '0' + m;
	}
	if (d < 10) {
		d = '0' + d;
	}
	return a.getFullYear() + '/' + m + '/' + d;
}

(function() {
	var _t = {};
	date_today = function(n) {
		if (_t.hasOwnProperty(n)) {
			return _t[n];
		}
		var tmp = numDate(dateNow(n)).split('/');
		return (_t[n] = [tmp[1],tmp[2],tmp[0]].join('/'));
	};
}());

function is_leap_year(y) {
	// leap year is every 4 years except for every 100th year except for every 400th year
	// ie 2000 = yes, 2100 = no, 2400 = yes
	return y % 400 === 0 ? true : y % 100 === 0 ? false : y % 4 === 0 ? true : false;
}

function dateFormat(format) {
	var f = [];
	switch (Number(format)) {
		case 1: f[0] = 1; f[1] = 2; f[2] = 0; break; //us - mm/dd/yyyy
		case 2: f[0] = 2; f[1] = 1; f[2] = 0; break; //eu - dd/mm/yyyy
		// case 0:  and
		default: f[0] = 0; f[1] = 1; f[2] = 2; break; //ux - yyyy/mm/dd
	}
	return f;
}

function dateCheck(value, format, separator) {
	if (!value) {
		return false;
	}

	var  date = value.split(separator),
		f = dateFormat(format),
		y = date[f[0]],
		m = date[f[1]],
		d = date[f[2]];

	if (y === '' || y < 1000 || y > 9999) {
		return false;
	}
	if (m === '' || m < 1 || m > 12) {
		return false;
	}
	if (d === '' || d < 1 || d > 31) {
		return false;
	}
	if (m === 2 && d > 29) {
		return false;
	}
	if (m === 2 && d > 28 && !is_leap_year(y)) {
		return false;
	}

	// February needs 29 days in the array below for leap years... February 29th for non-leap years is filtered out above
	if (d > [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m-1]) {
		return false;
	}

	return true;
}

function setbyvalue(obj, val) {
	var i, l, j, l2, elems, type = obj.type || (obj[0] ? obj[0].type : undefined);
	if (arguments.length === 1 || val === undefined) {
		val = null;
	}
	switch (type) {
		case 'radio':
		case 'checkbox':
			elems = obj.type ? obj.form[obj.name] : obj;
			l = elems.length;
			if (val === null) {
				for (i = 0; i < l; i++) {
					elems[i].checked = false;
				}
			} else if (val.constructor === Array) {
				for (j = 0, l2 = val.length; j < l2; j++) {
					for (i = 0; i < l; i++) {
						if (elems[i].value === val[j]) {
							elems[i].checked = true;
						}
					}
				}
			} else {
				for (i = 0; i < l; i++) {
					if (elems[i].value === val) {
						return (elems[i].checked = true);
					}
				}
			}
			break;
		case 'select-multiple':
		case 'select-one':
			obj.selectedIndex = -1;
			obj = obj.options;
			l = obj.length;
			if (val === null) {
				return true;
			} else if (val.constructor === Array) {
				for (j = 0, l2 = val.length; j < l2; j++) {
					for (i = 0; i < l; i++) {
						if (obj[i].value === val[j]) {
							obj[i].selected = true;
						}
					}
				}
			} else {
				for (i = 0; i < l; i++) {
					if (obj[i].value === val) {
						return (obj[i].selected = true);
					}
				}
			}
			break;
		case 'text':
		case 'textarea':
		case 'password':
		case 'hidden':
			obj.value = val === null ? '' : val.constructor === Array ? val[0] : val;
			break;
		default:
			alert('setbyvalue: unknown type [' + type + ']');
	}
	return true;
}

function select_all(oList) {
	var opt = oList.options, i, l;
	for (i = 0, l = opt.length; i < l; i++) {
		opt[i].selected = true;
	}

}

function populateList(oList, textList, valueList) {
	var i, a;
	oList.options.length = 0;
	if (!textList || textList.length === 0) {
		return;
	}
	if (textList.constructor === Array) {
		if (valueList) {
			for (i = 0; i < textList.length; i++) {
				oList.options[i] = new_Option(textList[i], valueList[i]);
			}
		} else if (textList[0].constructor === Array) {
			for (i = 0; i < textList.length; i++) {
				oList.options[i] = new_Option(textList[i][1], textList[i][0]);
			}
		} else {
			for (i = 0; i < textList.length; i++) {
				oList.options[i] = new_Option(textList[i], textList[i]);
			}
		}
	} else if (typeof(textList) === 'object') {
		a = 0;
		for (i in textList) {
			if (textList.hasOwnProperty(i) && textList[i].constructor !== Function) {
				oList.options[a++] = new_Option(textList[i], i);
			}
		}
	//} else {
		//alert("don't know what to do with " + typeof(textList))
		//alert(typeof(textList) === 'function')
	}
}

function _b_zero(v)  { if (v === '') { return 0      ; } else { return v; } }
function _b_abort(v) { if (v === '') { throw 'abort' ; } else { return v; } }
function _b_fail(v)  { if (v === '') { throw 'fail'  ; } else { return v; } }

/*
returns array of values of SELECTED items
works on select lists or check boxes
use: document.form1.selectList.getSelections = selections
or : selections(document.form1.selectList)
*/
function selections(obj, get, params) {
	if (this !== window) {
		params = get;
		get = obj;
		obj = this;
	}

	if (!params) {
		params = {};
	}

	var a, i, ret = [], type = obj.type || (obj[0] ? obj[0].type : undefined);
	if (!get) {
		get = 'value';
	}
	switch (get) {
		case 'index':
			get = function() { return i; };
			break;
		case 'text':
			get = function() { return a[i].text; };
			break;
		default:
			get = function() { return a[i].value; };
	}

	if (!type) {
		alert("can't discern type from " + obj);
		throw 'error';
	}
	if (type === 'checkbox') {
		a = obj.type ? obj.form[obj.name] : obj;
		for (i = 0; i < a.length; i++) {
			if (a[i].checked) {
				ret.push(get());
			}
		}
	} else if (type === 'select-multiple') {
		a = obj.options;
		for (i = 0; i < a.length; i++) {
			if (a[i].selected) {
				ret.push(get());
			}
		}
	} else if (type === 'select-one') {
		if (obj.selectedIndex === -1) {
			ret = '';
		} else {
			a = obj.options;
			i = obj.selectedIndex;
			ret = get();
		}
	} else if (type === 'radio') {
		a = obj.type ? obj.form[obj.name] : obj;
		for (i = 0; i < a.length; i++) {
			if (a[i].checked) {
				ret = get();
				break;
			}
		}
	} else if (type === 'text' || type === 'textarea' || type === 'password' || type === 'hidden') {
		ret = obj.value;
	} else {
		ret = null;
		alert('unknown element type: ' + type);
	}

	return params.force_array && ret.constructor !== Array ? [ ret ] : ret;
}

function _all(obj, get, return_type) {
	if (this !== window) {
		return_type = get;
		get = obj;
		obj = this;
	}

	var a, i, ret = [], type = obj.type || (obj[0] ? obj[0].type : undefined);
	if (!get) {
		get = 'value';
	}
	if (!return_type) {
		return_type = 'array';
	}
	switch (get) {
		case 'index':
			get = function() { return i; };
			break;
		case 'text':
			get = function() { return a[i].text; };
			break;
		case 'both':
			get = return_type === 'hash'
				? function() { return { value : a[i].value, text : a[i].text }; }
				: function() { return [ a[i].value, a[i].text ]; };
			break;
		default:
			get = function() { return a[i].value; };
	}

	if (!type) {
		alert("can't discern type from " + obj);
		throw 'error';
	}
	if (type === 'checkbox') {
		a = obj.type ? obj.form[obj.name] : obj;
		for (i = 0; i < a.length; i++) {
			ret.push(get());
		}
	} else if (type === 'select-multiple') {
		a = obj.options;
		for (i = 0; i < a.length; i++) {
			ret.push(get());
		}
	} else if (type === 'select-one') {
		a = obj.options;
		for (i = 0; i < a.length; i++) {
			ret.push(get());
		}
	} else if (type === 'radio') {
		a = obj.type ? obj.form[obj.name] : obj;
		for (i = 0; i < a.length; i++) {
			ret.push(get());
		}
	} else if (type === 'text' || type === 'textarea' || type === 'password' || type === 'hidden') {
		ret = obj.value;
	} else {
		ret = null;
		alert('unknown element type: ' + type);
	}

	return ret;
}

// this will be assigned by _sort_list_cmp()
function _sort_list_cmp(){}

function _sort_list_cmp_simple(a, b) {
	a = a.toLowerCase();
	b = b.toLowerCase();
	return a < b ? -1 : a > b ? 1 : 0;
}

function _sort_list_cmp_smart(_a, _b) {
	var a = _a.toLowerCase(), b = _b.toLowerCase(),
		x1, x2, y1, y2,
		long_a, long_b,
		l1, l2, s1, s2,
		i, l,
		n1, n2;
	if (a === b) {
		return 0;
	}
	if (a.match(/\d+/) && b.match(/\d+/)) {
		x1 = a.split(/\d+/);
		x2 = a.match(/(\d+)/g);
		y1 = b.split(/\d+/);
		y2 = b.match(/(\d+)/g);
		// if a is longer than b, then is should come 2nd
		if (x1.length >= y1.length) {
			long_a = 1;
			long_b = -1;
			l1 = x1;
			l2 = x2;
			s1 = y1;
			s2 = y2;
		} else {
			long_a = -1;
			long_b = 1;
			l1 = y1;
			l2 = y2;
			s1 = x1;
			s2 = x2;
		}
		for (i = 0, l = s1.length; i < l; i++) {
			if (l1[i] !== s1[i]) {
				return l1[i] < s1[i] ? long_b : long_a;
			}
			n1 = Number(l2[i]);
			n2 = Number(s2[i]);
			if (n1 !== n2) {
				return n1 < n2 ? long_b : long_a;
			}
		}
		return long_a;
	} else {
		return a < b ? -1 : 1;
	}
}

function _sort_list_text_a  (a, b) { return _sort_list_cmp(a[1], b[1]); }
function _sort_list_value_a (a, b) { return _sort_list_cmp(a[0], b[0]); }
function _sort_list_text_h  (a, b) { return _sort_list_cmp(a.text, b.text); }
function _sort_list_value_h  (a, b) { return _sort_list_cmp(a.value, b.value); }

function sort_list(obj, options) {
	if (this !== window) {
		options = obj;
		obj = this;
	}

	// returns [value, text]
	var data = _all(obj, 'both', 'array'), sorted;

	_sort_list_cmp = _sort_list_cmp_simple; 
	if (options.smart) {
		// pre-sort with quick match so we're quicker
		sorted = data.sort(_sort_list_text_a);
		_sort_list_cmp = _sort_list_cmp_smart;
		sorted = sorted.sort(_sort_list_text_a);
	} else {
		sorted = data.sort(_sort_list_text_a);
	}

	if (options.reverse) {
		sorted = sorted.reverse();
	}

	populateList(obj, sorted);
}

function move_options(src, dest, try_insert) {
	var fields = selections(src, 'index'), z, x, tmp;
	if (fields.length === 0) {
		alert('Nothing selected.');
		return false;
	}

	if (try_insert && dest.selectedIndex !== -1) {
		z = dest.selectedIndex;
		for (x = 0; x < fields.length; x++) {
			tmp = src.options[fields[x]];
			dest.options.add(new_Option(tmp.text, tmp.value), z);
			//dest.options[z].selected = true;
		}
	} else {
		z = dest.options.length;
		for (x = 0; x < fields.length; x++, z++) {
			tmp = src.options[fields[x]];
			dest.options[z] = new_Option(tmp.text, tmp.value);
			//dest.options[z].selected = true;
		}
	}

	for (x = fields.length - 1; x >= 0; x--) {
		src.options[fields[x]] = null;
	}

	return true;
}

function swap_options(first, second) {
	var v = second.value, t = second.text, s = second.selected;

	second.value = first.value;
	second.text = first.text;
	second.selected = first.selected;
	first.value = v;
	first.text = t;
	first.selected = s;
}

function option_up(oList) {
	var x = 0, i, l;
	oList = oList.options;
	while (oList[x].selected) {
		x++;
	}

	for (i = x, l = oList.length; i < l; i++) {
		if (oList[i].selected) {
			swap_options(oList[i], oList[i - 1]);
		}
	}
}

function option_down(oList) {
	var x = 1, i;
	oList = oList.options;
	while (oList[oList.length - x].selected) {
		x++;
	}

	for (i = oList.length - x; i >= 0; i--) {
		if (oList[i].selected) {
			swap_options(oList[i], oList[i + 1]);
		}
	}
}

function getindexbyvalue(oList, val) {
	var i;
	oList = oList.options;
	for (i = 0; i < oList.length; i++) {
		if (oList[i].value === val) {
			return i;
		}
	}
	return -1;
}

function option_delete(list) {
	var o = list.options, i;
	for (i = o.length - 1; i >= 0; i--) {
		if (o[i].selected) {
			o[i] = null;
		}
	}
}

function option_add(list, text, value) {
	if (arguments.length < 3) {
		value = text;
	}
	var new_opt = new_Option(text, value),
		idx = list.selectedIndex;

	if (idx !== -1) {
		list.options.add(new_opt, idx);
	} else {
		list.options[list.options.length] = new_opt;
	}
}

function option_update(list, text, value) {
	if (arguments.length < 3) {
		value = text;
	}
	var idx = list.selectedIndex, opt;
	if (idx === -1) {
		return;
	}
	opt = list.options[list.selectedIndex] = new_Option(text, value);
	opt.selected = true;
}

function checkEmail(address) {
	if ( ! (/\w\S*@\S+\.\w\w+/).test(address) ) {
		alert("Invalid e-mail address:\n\n'" + address + "'");
		return false;
	}
	return true;
}
function checkEmails(addresses) {
	if (addresses === '') {
		return true;
	}
	var address = addresses.replace(/\n/g, ',').replace(/ /g, '').replace(/;/g, ',').replace(/,,+/g, ',').split(/,/),
		i, l;
	for (i = 0, l = address.length; i < l; i++) {
		if ( ! checkEmail(address[i]) ) {
			return false;
		}
	}

	return true;
}

function form_obj(elem_id) {
	var obj = {
		'project_id' : (df.projectID ? df.projectID.value : 0),
		// project creation form doesn't use FormID presently
		'form_id'    : (df.FormID ? df.FormID.value : 0),
		'elem_id'    : elem_id,
		'entry_id'   : (df.EntryID ? df.EntryID.value : 0),
		// unique_id doesn't necessarily exist on form_builder_mass.cgi
		// so use a conditional
		'unique_id'  : (df.unique_id ? df.unique_id.value : ''),
		'pass'       : (df.pass ? df.pass.value : '')
	};

	obj.qs = [
		'&projectID=', obj.project_id,
		'&FormID=', obj.form_id,
		'&pass=', obj.pass
	].join('');

	obj.rs = [
		'&p0=', obj.project_id,
		'&p1=', obj.form_id,
		'&p2=', obj.pass,
		'&p3=', obj.entry_id,
		'&p4=', obj.elem_id,
		'&p5=', obj.unique_id
	].join('');

	return obj;
}

function datePick(cal, date) {
	var elem, method, fmt, separator, delta, d, i;
	if (!cal.dateClicked) {
		return;
	}
	elem = cal.__elem;
	method = cal.__method;
	fmt = cal.__format;
	separator = cal.__separator;
	delta = cal.__delta;

	d = date.split('/');
	if (method === 'select') {
		for (i = 0; i < 3; i++) {
			switch (fmt[i]) {
				case 0: setbyvalue(elem[i + 3 * delta], d[0]); break;
				case 1: elem[i + 3 * delta].selectedIndex = d[1]; break;
				case 2: elem[i + 3 * delta].selectedIndex = d[2]; break;
			}
		}
	} else {
		//d = [year, month, day]
		if (elem.length) {
			elem[delta].value = [d[fmt[0]], d[fmt[1]], d[fmt[2]]].join(separator);
		} else {
			elem.value = [d[fmt[0]], d[fmt[1]], d[fmt[2]]].join(separator);
		}
	}
	cal.callCloseHandler();
}

function closeHandler(cal) {
	cal.hide();
	calendar = null;
}

function datePicker(src, elem_name, method, format, separator, delta) {
	var elem = src.form[elem_name], d, m, y, fmt, spread, i, txt, date, dateStr, cal;
	delta = Number(delta) || 0;
	format = Number(format);
	fmt = dateFormat(format);
	if (method === 'select') {
		//alert((Number(extra) || 0))
		spread = 3 * delta + 2;
		for (i = 0; i < 3; i++) {
			switch (fmt[i]) {
				case 0: y = elem[i + 3 * delta].value; break;
				case 1: m = elem[i + 3 * delta].value; break;
				case 2: d = elem[i + 3 * delta].value; break;
			}
		}
	} else {
		// method === 'text'
		if (elem.length) {
			spread = delta;
			txt = elem[delta].value;
		} else {
			txt = elem.value;
		}
			
		if (txt) {
			date = txt.split(separator);
			for (i = 0; i < 3; i++) {
				switch (fmt[i]) {
					case 0: y = date[i]; break;
					case 1: m = date[i]; break;
					case 2: d = date[i]; break;
				}
			}
		}
		
	}

	dateStr = y && m && d ? [y, m, d].join('/') : '';

	// delete any previous calendar
	if (calendar) {
		calendar.hide();
	}
	calendar = new Calendar(1, null, datePick, closeHandler);
	cal = calendar;
	cal.showsTime = cal.time24 = false;
	cal.showsOtherMonths = true;
	cal.setRange(1900, 2050);
	cal.create();
	cal.setDateFormat('%Y/%m/%d');
	cal.parseDate(dateStr);
	cal.__elem = elem;
	cal.__method = method;
	cal.__format = fmt;
	cal.__separator = separator;
	cal.__delta = delta;

	// display calendar (B)ottom, (r)ight-aligned of the nextSibling
	cal.showAtElement((elem.length ? elem[spread] : elem).nextSibling.nextSibling);

	return false;
}

function dateError(format, separator) {
	return (
		[
			[ 'yyyy', 'mm', 'dd, 1000', '01', '01 - 9999', '12', '31' ],
			[ 'mm', 'dd', 'yyyy, 01', '01', '1000 - 12', '31', '9999' ],
			[ 'dd', 'mm', 'yyyy, 01', '01', '1000 - 31', '12', '9999' ]
		][Number(format)]
	).join(separator);
}

/*  File Element */

function append_unique_id(href) {
	var id = '&unique_id=' + df.unique_id.value;
	if (!href.match(/&unique_id=/)) {
		if (href.indexOf('#') !== -1) {
			href = href.replace('#', id + '#');
		} else {
			href += id;
		}
	} else {
		href = href.replace(/&unique_id=[^&]*/, id);
	}
	return href;
}

function f_callback_monitor() {
	if (!f_callback_data) {
		return;
	}

	// after waiting 3 seconds for the waiting.html callback,
	// try to do one ourselves
	if (f_callback_data.refresh_files_called && f_callback_data.refresh_files_called.getTime() + 3000 < (new Date()).getTime()) {
		$('input[type="file"]').each(function() { 
			refresh_files(this.name);
		});
		f_callback_data = null;
		return;
	}

	window.setTimeout(f_callback_monitor, 1000);
}

function upload_file(elem_id, __submit) {
	var file = df[elem_id], w;
	if (file.value === '') {
		alert('No file selected to upload!');
		return false;
	}

	if (file.validate() === false) {
		return false;
	}

	df[elem_id + "_file_action.value"] = 'add_attachment';
	w = oWin.open('cgihtml/waiting.html?unique_id=' + df.unique_id.value + '&callback=f_callback','upload','toolbar=no,location=no,directories=no,status=no,menubar=no,copyhistory=no,scrollbars=no,width=300,height=150');

	f_callback_data = {
		action: df.action,
		window: w
	};

	f_callback_monitor();

	// must explicilty be false
	if (__submit !== false) {
		df.action = append_unique_id(_loc.href);
		df._submit.value = 1;
		df.submit();
	}
	return true;
}

function view_file(elem_id) {
	// we can at least check for this one easily
	var oList = df[elem_id + '_attachments'], filename;

	if (oList.selectedIndex === -1) {
		alert('You need to select a file to view.');
		return false;
	} else if (oList.value === 'NoFiles') {
		alert('No attached files to view.');
		return false;
	}

	filename = oList.value;

	oWin.open('/projdata/temp/' + filename, 'ViewFile', 'width=640,height=480,resizable=1');
	return false;
}

function view_f(elem_id) {
	view_file(elem_id);
	return false;
}

function multi_callback_async(obj, data, extras) {
	var files = evil(data), oList = df[obj.elem_id + '_attachments'];
	populateList(oList, files[0], files[1]);
	if (extras.wipe_f_callback) {
		f_callback_data = null;
	}
}

function single_callback_async(obj, data, extras) {
	var     files = evil(data),
		elem = df[obj.elem_id + '_attachments'],
		node = document.getElementById(obj.elem_id + '_current'),
		a;
	if (files.length && files[1][0] !== 'NoFiles') {
		a = node.getElementsByTagName('a')[0];
		a.href = files[1][0];
		a.innerHTML = files[0][0];
		elem.value = files[1][0];
	} else if (node) {
		node.innerHTML = '';
	}
	if (extras.wipe_f_callback) {
		f_callback_data = null;
	}
}

function rs_remove_attachments(obj, callback, extras) {
	if (!callback) {
		callback = multi_callback_async;
	}
	if (!extras) {
		extras = {};
	}

	rs_get_data(
		// url
		[
		url_prefix + 'form_builder.cgi?_method=RS_remove_attachments',
			'&p0=', obj.project_id,
			'&p1=', obj.form_id,
			'&p2=', obj.pass,
			'&p3=', obj.entry_id,
			'&p4=', obj.elem_id,
			'&p5=', obj.unique_id,
			'&p6=', obj.values.join(',')
		].join(''),

		// data (for POST)
		null,

		// callback function
		function(data) { callback(obj, data, extras); }
	);
}

function remove_file(elem_id) {
	var attach, files, single, obj;
	// we can at least check for this one easily
	if (df[elem_id].value !== '') {
		upload_file(elem_id, false);
	}

	attach = df[elem_id + "_attachments"];

	files = selections(attach);

	single = Boolean(attach.type === 'hidden');

	if (single) {
		if (files === '') {
			return false;
		}
	} else if (files.length === 0) {
		alert('You need to select one or more files to remove.');
		return false;
	} else if (files[0] === 'NoFiles') {
		alert('No attached files to remove.');
		return false;
	}

	obj = form_obj(elem_id);
	obj.values = single ? [ files ] : files;

	if (single) {
		rs_remove_attachments(obj, single_callback_async);
	} else {
		rs_remove_attachments(obj);
	}

	return false;
}

function remove_f(elem_id) {
	remove_file(elem_id);
	return false;
}

function rs_get_attachments(obj, callback, extras) {
	if (!callback) {
		callback = multi_callback_async;
	}
	if (!extras) {
		extras = {};
	}

	rs_get_data(
		// url
		[
		url_prefix + 'form_builder.cgi?_method=RS_get_attachments',
			'&p0=', obj.project_id,
			'&p1=', obj.form_id,
			'&p2=', obj.pass,
			'&p3=', obj.entry_id,
			'&p4=', obj.elem_id,
			'&p5=', obj.unique_id
		].join(''),

		// data (for POST)
		null,

		// callback function
		function(data) { callback(obj, data, extras); }
	);
}

function refresh_files(elem_id, extras) {
	var obj = form_obj(elem_id);
	if (extras && extras.wipe_f_callback) {
		f_callback_data.refresh_files_called = new Date();
	}
	if (df[elem_id + "_attachments"].type === 'hidden') {
		rs_get_attachments(obj, single_callback_async, extras);
	} else {
		rs_get_attachments(obj, multi_callback_async, extras);
	}
	return false;
}

function f_callback(obj) {
	window.setTimeout(function() { obj.child.close(); }, 200);
	df.action = f_callback_data.action;

	var file_elements = $('input[type="file"]');
	file_elements.each(function(i) { 
		refresh_files(this.name, {wipe_f_callback : i === file_elements.length - 1});
		var clone = this.cloneNode(true);
		try {
			// FF 3.5? (3.0?) started doing full clones
			if (clone.value) {
				clone.value = '';
			}
		} catch (e) { }
		clone.validate = this.validate;
		this.parentNode.replaceChild(clone, this);
	});

	//f_callback_data = null;
}

function do_multi(elem_id, fileTypes) {
	var obj = form_obj(elem_id), cb, params;
	obj.prefix = [obj.project_id, obj.form_id, obj.entry_id, obj.elem_id, obj.unique_id];

	cb = function(args) {
		obj.args = args;
		rs_get_attachments(obj);
	};
	params = {
		prefix   : obj.prefix,
		callback : cb 
	};

	//params.fileTypes   : "All Files:*,AutoCAD Files (=v1):*.dwg;*.dwf;*.dwx,Image Files:*.jpg;*.png;*.gif;*.bmp;*.ico",
	if (fileTypes) {
		params.fileTypes = fileTypes;
	}

	oWin.showDialog(
		'cgihtml/fupload.html',
		params,
		'dialogHeight:340px;dialogWidth:310px'
	);
	return false;
}

function rs_proxy(elem_id, method, params, callback) {
	var obj = form_obj(elem_id), _callback, extra_args = '', i;
	if (callback) {
		_callback = function (data) { callback(obj, data); };
	}

	for (i in params) {
		if (params.hasOwnProperty(i) && params[i].constructor !== Function) {
			extra_args += '&' + i + '=' + params[i];
		}
	}
	return rs_get_data(
		// url
		[
			url_prefix + 'form_builder.cgi?_method=rs_proxy',
			obj.rs,
			'&elem_method=', method,
			extra_args
		].join(''),

		// data (for POST)
		null,

		// callback function
		_callback
	);
}

function check_auth(elem_id, pass, callback) {
	if (!window.encrypt) {
		throw new Error('Unable to send password securely. Aborting.');
	}
	return rs_proxy.apply(null,
		[
			// standard args
			elem_id, 'check_auth', { pass : encrypt(pass, getCookie('SessionID')) }
		].concat(
			// optional args
			callback ? [callback] : []
		)
	);
}

function disable(obj) {
	if (!obj) {
		obj = this;
	}
	switch (obj.type) {
		case 'button':
			obj.onclick = null;
			break;
		case 'select-multiple':
			break;
		default:
	}
	obj.disabled = true;
}

function enable(obj, extra) {
	if (!obj) {
		obj = this;
	}
	switch (obj.type) {
		case 'select-multiple':
			if (extra) {
				obj.onchange = extra;
			}
			break;
		case 'button':
			if (extra) {
				obj.onclick = extra;
			}
			break;
		case 'text':
			break;
		default:
	}
	obj.onfocus = null;
	obj.disabled = false;
}

function stop_propagation(e) {
	if (e.preventDefault) {
		e.preventDefault();
		e.stopPropagation();
	} else {
		e.returnValue = false;
		e.cancelBubble = true;
	}
	return false;
}

if (window.addEventListener) {
	window.add_event = function (obj, evt, fn) { obj.addEventListener(evt, fn, false); };
	window.del_event = function (obj, evt, fn) { obj.removeEventListener(evt, fn, false); };
} else {
	window.add_event = function (obj, evt, fn) { obj.attachEvent('on' + evt, fn); };
	window.del_event = function (obj, evt, fn) { obj.detachEvent('on' + evt, fn); };
}

function opt_groups(sel_list) {
	var x = _all(sel_list), z = [], t = [], og, i, ogg, j;

	og = document.createElement('optgroup');

	// cycle through backwards looking for {text}
	for (i = x.length - 1; i >= 0; i--) {
		if (x[i].indexOf('{') === 0) {
			ogg = og.cloneNode(true);
			ogg.label = x[i].replace(/[}{]/g, '');
			for (j = t.length - 1; j >= 0; j--) {
				ogg.appendChild(new_Option(t[j], t[j], true));
			}
			t = [];
			z.push(ogg);
		} else {
			t.push(x[i]);
		}
	}
	if (t.length) {
		for (j = t.length - 1; j >= 0; j--) {
			z.push(new_Option(t[j], t[j], true));
		}
	}
	sel_list.options.length = 0;
	for (i = z.length - 1; i >= 0; i--) {
		sel_list.appendChild(z[i]);
	}
}

(function() {
	var     option = document.createElement('option'),
		_div = document.createElement('div');

	new_Option = function() {
		var opt;
		if (window.$ && $.browser.msie) {
			new_Option = function(text, value, dirty_ie) {
				if (arguments.length === 1) {
					value = text;
				} else if (arguments.length > 2 && dirty_ie) {
					opt = new Option(text, value);
					opt.appendChild(document.createTextNode(text));
					return opt;
				}
				return new Option(text, value);
			};
		} else {
			new_Option = function(text, value) {
				opt = option.cloneNode(true);
				opt.text = text;
				opt.value = arguments.length > 1 ? value : text;
				return opt;
			};
		}
		return new_Option.apply(this, arguments);
	};
	new_Options = function(opts) {
		var html = ['<select>'], l = opts.length, i, opt;
		if (opts[0].hasOwnProperty('value')) {
			for (i = 0; i < l; i++) {
				opt = opts[i];
				html = html.concat(['<option value="', opt.value, '">', opt.text, '</option>']);
			}
		} else {
			for (i = 0; i < l; i++) {
				opt = opts[i];
				html.concat(['<option>', opt.text, '</option>']);
			}
		}
		html.push('</select>');
		_div.innerHTML = html.join('');
		return _div.firstChild.childNodes;
	};
}());

(function() {
	var cache = {in_elem:{},out_elem:{},pass:{}}, signature_callback;

	signature_callback = function(obj, data, not_forced) {
		var ret = evil(data), params, password;
		params = { message: 'Password is ' + (ret ? 'valid' : 'invalid'), css: { fontFamily : 'Arial' } };
		if (not_forced) {
			params.timeout = 3000;
		} else {
			params.message += '<br /><input type="button" value="OK" onclick="$.unblockUI()">';
		}
		$.blockUI(params);
		password = cache.in_elem[obj.elem_id].value;
		cache.out_elem[obj.elem_id].value = encrypt(password, getCookie('SessionID'));
		if (ret) {
			cache.pass[password] = true;
		}
		return ret;
	};

	signature_check = function(elem, force) {
		var pass_input, password, ret;
		if (cache.in_elem.hasOwnProperty(elem.name)) {
			pass_input = cache.in_elem[elem.name];
		} else {
			pass_input = document.getElementById(elem.name + '_pass');
			cache.in_elem[elem.name] = pass_input;
			cache.out_elem[elem.name] = elem;
		}

		password = pass_input.value;

		if (password === '') {
			if (force) {
				alert('Password can not be empty');
				return false;
			}
			// if we weren't forced into this, then we probably are not approving
			return true;
		}

		if (cache.pass.hasOwnProperty(password)) {
			if (!force) {
				return true;
			}
			delete cache.pass[password];
		}

		if (!window.encrypt) {
			alert('Unable to send password securely. Aborting.');
			return false;
		}

		$.blockUI({message: 'Validating password', css: { fontFamily : 'Arial' } });

		try {
			ret = check_auth.apply(null, [elem.name, password].concat(force ? [signature_callback] : []));
		} catch(e) {
			alert(e.message);
			$.unblockUI({fadeOut: 0});
			return false;
		}

		if (force) {
			return false;
		}

		return signature_callback(form_obj(elem.name), ret, true);
	};

}());

(function() {

	QueryString = function(qs) {
		this.query = qs || window.location.search.substring(1);
		this.names = [];
		this.params = {};
		this.processed = false;
	};

	// for perl likeness
	CGI = QueryString;

	var p = QueryString.prototype;
	p.process = function() {
		if (this.processed) {
			return;
		}
		this.processed = true;
		if (!this.query) {
			return;
		}

		var chunks = this.query.split(re_amp), i, l, pair, v, tmp;

		for (i = 0, l = chunks.length; i < l; i++) {
			pair = chunks[i].split(re_eq, 2);
			v = unescape(pair[1]);
			if (this.params.hasOwnProperty(pair[0])) {
				tmp = this.params[pair[0]];
				if (tmp.constructor === Array) {
					tmp.push(v);
				} else {
					tmp = [ tmp, v];
					this.params[pair[0]] = tmp;
				}
			} else {
				this.names.push(pair[0]);
				this.params[pair[0]] = v;
			}
		}
	};

	p.param = function(name, value) {
		this.process();
		if (!name) {
			return this.names;
		}
		if (arguments.length === 2) {
			if (this.params.hasOwnProperty(name) === false) {
				this.names.push(name);
			}
			return (this.params[name] = value);
		} else {
			return this.params[name];
		}
	};

	p.toString = function() {
		var str = [], i, l, a, b, j, k;
		for (i = 0, l = this.names.length; i < l; i++) {
			a = this.names[i];
			b = this.params[a];
			if (b.constructor === Array) {
				for (j = 0, k = b.length; j < k; j++) {
					str.push(a + '=' + b[j]);
				}
			} else {
				str.push(a + '=' + b);
			}
		}
		return str.join('&');
	};

}());

// static class
USGN.util = {
	log : null,
	warn : null,
	error : null,
	uc          : function(t){},
	ucfirst     : function(t){},
	ncmp        : function(a, b){},
	repeat      : function(s, n){},
	seq         : function(s, e){},
	printf      : function(str){},
	format_date : function(d){},
        format_time : function(s){},
	slice       : function(obj, keys){},
	keys        : function(obj){},
	values      : function(obj){},
	first       : function(obj, fn){},
	firstidx    : function(obj, fn){},
	grep        : function(obj, fn){},
	uniq        : function(obj, key_fn){},
	map         : function(obj, map_fn){},
	reduce      : function(obj, fn, initial_value, extended) {}
};

(function() {
	var _self = USGN.util, uc, printf;

	_self.log = window.console ? function(msg) { window.console.log(msg); } : function() {};
	_self.warn = _self.log;
	_self.error = function(msg) { _self.log(msg); alert(msg); };

	// because of String.replace(/(.)/, '$1') this wrapper fn is needed
	_self.uc = uc = function(t)    { return t.toUpperCase(); };
	_self.ucfirst = function(t)    { return t.charAt(0).toUpperCase() + t.substring(1); };
	_self.ncmp    = function(a, b) { return a - b; };
	_self.repeat  = function(s, n) { return (new Array(n + 1)).join(s); };
	_self.seq     = function(s, e) {
		var a = [];
		for (; s <= e; s++) {
			a.push(s);
		}
		return a;
	};

	// printf.js was too slow. this is not very functional, but much faster
	_self.printf = printf = function(str) {
		var tokens = str.match(/(%\d*\w)/g), i = 0, l = tokens.length;
		for (; i < l; i++) {
			var token = tokens[i];
			var arg = arguments[i + 1];

			if (/%[sd]/.test(token)) {
				str = str.replace(token, arg ? arg : arg === 0 ? arg : '');
				continue;
			}

			var toks = token.match(/%(\d*)(\w)/);
			var flags = toks[1], type = toks[2];
			if (type !== 'd' || !flags) {
				str = str.replace(token, arg ? arg : arg === 0 ? arg : '');
				continue;
			}

			// assume %02d
			if (arg < 10) {
				arg = '0' + arg;
			}
			str = str.replace(token, arg);
		}
		return str;

	};

	_self.format_date = function(d) {
		return printf("%02d/%02d %02d:%02d:%02d", d.getMonth() + 1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds());
	};

	_self.format_time = function(s) {
		var sign = _b(s < 0);
		if (sign) {
			s = -s;
		}
		var d = new Date(s * 1000);
		return (d.getUTCFullYear() > 1970 ? (d.getUTCFullYear() - 1970) + 'y, ' : '')
			+ (s >= 86400 ? Math.floor( (d - (Date.UTC(d.getUTCFullYear(), 0, 1))) / 86400000) + 'd, ' : '')
			+ printf("%s%02d:%02d:%02d", (sign ? '- ' : ''), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds());
	};

	_self.slice = function(obj, keys) {
		if (!keys) {
			return;
		}
		if (keys.constructor !== Array) {
			keys = $.makeArray(arguments);
			keys.shift(); // get rid of obj
		}
		return $.map(keys, function(key) { return [obj[key]]; });
	};

	_self.keys = keys = function(obj) {
		if (Object.keys) {
			return Object.keys(obj);
		}

		var c = [], i;
		for (i in obj) {
			if (obj.hasOwnProperty(i)) {
				c.push(i);
			}
		}
		return c;
	};

	_self.values = values = function(obj) {
		var c = [], i;
		for (i in obj) {
			if (obj.hasOwnProperty(i)) {
				c.push(obj[i]);
			}
		}
		return c;
	};

	// this works on objects and arrays, but objects won't necessarily be stable results
	// ie, we could get different value on subsequent calls of first()
	_self.first = first = function(obj, fn) {
		var _first;
		$.each(obj, function(i, val) {
			if (fn(val, i) === true) {
				_first = val;
				// foreach::last
				return false;
			}
		});
		return _first;
	};

	// this works on objects and arrays, but objects won't necessarily be stable results
	// ie, we could get different value on subsequent calls of firstidx()
	_self.firstidx = firstidx = function(obj, fn) {
		var _firstidx = -1;
		$.each(obj, function(i, val) {
			if (fn(val, i) === true) {
				_firstidx = i;
				// foreach::last
				return false;
			}
		});
		return _firstidx;
	};

	// this works on objects and arrays, but objects won't necessarily be stable results
	// ie, we could get different value on subsequent calls of grep()
	_self.grep = grep = function(obj, fn) {
		if (obj.constructor === Array) {
			return $.grep(obj, fn);
		}

		var ret = [];
		$.each(obj, function(i, val) {
			if (fn(val, i) === true) {
				ret.push(val);
			}
		});
		return ret;
	};

	// this works on objects and arrays
	_self.uniq = uniq = function(obj, key_fn) {
		var uniqs = {};
		if (!key_fn) {
			key_fn = function(x) { return x; };
		}
		$.each(obj, function(orig_key, value) {
			var key = key_fn(value, orig_key);
			if (!uniqs.hasOwnProperty(key)) {
				uniqs[key] = value;
			}
		});

		return _self.values(uniqs);
	};

	// this works on objects and arrays
	// $.map() doesn't work on objects unless they are modeled like arrays
	_self.map = map = function(obj, map_fn) {
		if (obj.constructor === Array) {
			return $.map(obj, map_fn);
		}

		var mapped = [];
		$.each(obj, function(i, val) {
			mapped = mapped.concat(map_fn(val, i));
		});
		return mapped;
	};

	// reduce({a : 1, b : 2, c : 3}, function(p, n) { return p + n; })    ===  6
	// reduce({b : 2, c : 3, d : 4}, function(p, n) { return p + n; }, 1) === 10
	_self.reduce = reduce = function(obj, fn, initial_value, extended) {
		if (obj.reduce) {
			// native reduce
			return arguments.length >= 3 ? obj.reduce(fn, initial_value) : obj.reduce(fn);
		}

		var accumulator, i, l, arr = Boolean(obj.constructor === Array), c = arr ? obj : keys(obj);

		if (arguments.length >= 3) {
			accumulator = initial_value;
			i = 0;
		} else {
			accumulator = arr ? c[0] : obj[c[0]];
			i = 1;
		}

		if (arr) {
			if (extended) {
				for (l = c.length; i < l; i++) {
					accumulator = fn(accumulator, c[i], i, c);
				}
			} else {
				for (l = c.length; i < l; i++) {
					accumulator = fn(accumulator, c[i]);
				}
			}
		} else {
			if (extended) {
				for (l = c.length; i < l; i++) {
					// fn(previous_value, current_value, current_key, object, key_index, key_array)
					accumulator = fn(accumulator, obj[c[i]], c[i], obj, i, c);
				}
			} else {
				for (l = c.length; i < l; i++) {
					// fn(previous_value, current_value)
					accumulator = fn(accumulator, obj[c[i]]);
				}
			}
		}

		return accumulator;
	};

	_self.timer = timer = function(fn) {
		var functional_timer;
		if (fn) {
			functional_timer = new timer();
			functional_timer.start()
			fn();
			return functional_timer.stop();
		}
	};
	(function (tp) {
		tp.start = function() {
			this._start = new Date();
			this._stop = null;
			this._laps = [];
		};

		tp.lap = function(diff) {
			if (!this._start) {
				return false;
			}
			var lap = new Date();
			this._laps.push(lap);
			return diff && laps.length > 1 ? lap - this._laps[this.laps.length - 2] : lap - this._start;
		};

		tp.stop = function() {
			if (!this._start) {
				return false;
			}
			this._stop = new Date();
			return this._stop - this._start;
		};

		tp.time = function() {
			return this._stop - this._start;
		};
	}(timer.prototype));

}());

$$ = USGN.util;

// static class
USGN.storage = {
	localStore : null,
	store_type : null,
	can_store  : null,
	store      : null,
	retrieve   : null,
	clear      : null,
	set_up_local_store : null
};

(function() {
	var _self = USGN.storage, localStore,
		_ST_X = -1, _ST_NONE = 0, _ST_IE = 1, _ST_MOZ = 2, _ST_STD = 3,
		store_x;

	_self._log = '';
	_self.log = function(x) { _self._log += x.constructor === Array ? x.join(' ') : x; };

	_self.set_up_local_store = function() {
		if (!$.browser.msie) {
			_self.log('browser is not ie, local_store not necessary');
			return true;
		}
		localStore = _self.localStore = $('#local_store').get(0);
		if (localStore) {
			_self.log('local_store already exists');
			return true;
		}

		_self.log('browser is ie and local_store does not exist');

		$(document.body).append('<div id="local_store" style="behavior:url(#default#userData);"></div>');

		_self.log('added local_store');

		localStore = _self.localStore = $('#local_store')[0];
	};

	_ST_X    = -1;
	_ST_NONE = 0;
	_ST_IE   = 1;
	_ST_MOZ  = 2;
	_ST_STD  = 3;
	_self.store_type = function() {
		if (_ST_X !== -1) {
			return _ST_X;
		}
		if (!window.$) {
			USGN.util.error('jQuery needed for this to complete');
		}
		if (typeof window.localStorage !== 'undefined') {
			store_x = window.localStorage;
			return (_ST_X = _ST_STD);
		}
		if (typeof window.globalStorage !== 'undefined') {
			store_x = window.globalStorage[window.location.hostname];
			return (_ST_X = _ST_MOZ);
		}
		if ($.browser.msie && !localStore) {
			_self.set_up_local_store();
		}
		if (localStore && typeof(localStore.XMLDocument) !== 'undefined') {
			return (_ST_X = _ST_IE);
		}
		return (_ST_X = _ST_NONE);
	};

	_self.store_type_text = function() { return [ 'None', 'IE Session', 'Mozilla Session', 'Standard Session (best)' ][_self.store_type()]; };

	_self.can_store = function() { return _self.store_type() !== _ST_NONE ? true : false; };
}());


// jslint stuff
/*jslint evil: true */
/*global window,console,alert,df,document,Calendar,oWin,rs_get_data,encrypt,getCookie,unescape,$ */
/*members $1, EntryID, FormID, __delta, __elem, __format, __method, 
    __separator, _d, _submit, _x2, action, add, addEventListener, add_event, 
    appendChild, apply, args, attachEvent, blockUI, callCloseHandler, 
    callback, cancelBubble, checked, child, cloneNode, close, concat, 
    console, constructor, create, createElement, createTextNode, css, 
    dateClicked, del_event, detachEvent, disabled, each, elem_id, encrypt, 
    entry_id, fadeOut, fileTypes, filter, floor, fontFamily, force_array, 
    form, form_id, getDate, getElementById, getElementsByTagName, 
    getFullYear, getMonth, getTime, getTimezoneOffset, hide, href, in_elem, 
    indexOf, innerHTML, join, length, location, log, match, message, name, 
    names, nextSibling, onchange, onclick, onfocus, open, options, out_elem, 
    param, params, parentNode, parseDate, pass, prefix, preventDefault, 
    process, processed, projectID, project_id, prototype, push, qs, query, 
    removeEventListener, replace, replaceChild, returnValue, rs, search, 
    selected, selectedIndex, setDateFormat, setRange, setTimeout, 
    showAtElement, showDialog, showsOtherMonths, showsTime, split, 
    stopPropagation, submit, substring, test, text, time24, timeout, 
    toString, toUpperCase, type, unblockUI, unique_id, validate, value, 
    values
*/

