/*  Dynamic Form JavaScript Framework, version 1.0.0.0
 *  (c) 2008-2009 David A Striegel
 *
/* ========================================================================== */

// code yanked from the Yahoo media player. Thanks, Yahoo.
if ( !("console" in window) || !("firebug" in console)) {
    var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
    window.console = {};
    for (var i=0; i<names.length; ++i) 
		window.console[names[i]] = function(){};
}

var DF = {
	version: '1.0.0.0',
	debug: false,
	layouts: false,
	maxTabs: 5,
	masterFormPath: '/secure/forms/master_form.php',
	datasetQueryPath: '/secure/_queries/_queries.php'
}

/* ========================================================================== */

DF.Tabs = Class.create({
	initialize: function(options) {
		this.setOptions(options);
		this.type = 'tabs';

		this.id = this.options.id;
		this.x = this.options.x;
		this.y = this.options.y;
		this.z = this.options.z;
		this.visible = this.options.visible;
		
		this.create();
	},
	
	setOptions: function(options) {
		this.options = {
			tabs: 		new Array(),
			
			x:			0,
			y:			0,
			z:			1,
			position:	'',

			form:		{id:''},
			name:		'',
			id:			DF.Utilities.uid(),
			parentid:	'',

			cssClass:	'subTabs',

			visible:	true
		}
		Object.extend(this.options, options || {});
	},	

	create: function() {
		var nContainer = new Element('div', {
			id: this.id,
			className: this.options.cssClass
		});
		if (this.options.position == 'absolute')
			nContainer.setStyle({
				position: 'absolute',
				top: this.y+'px',
				left: this.x+'px',
				zIndex: this.z
		});

		$(this.options.parentid).appendChild(nContainer);
	},

	hideControls: function() {
		$A(this.options.tabs).each( function(item) { item.hide(); });
	},

	hide: function() {
		this.hideControls();
		$(this.id).update('');
	},

	showControls: function() {
		$(this.id).setStyle({ visibility:'hidden' });
		$(this.id).update('');

		var _this = this;
		$A(this.options.tabs).each(function(item) {
			if (!item.hidden) {
				var nLink = new Element('a', {
					href: 'javascript://',
					containerid: item.id,
					className: 'tab'+((item.options.active)?' on':'')
				}).update(item.options.title);
	
				$(_this.id).appendChild(nLink);
	
				DF.Utilities.addEvent(nLink, 'click', function(e) {
					if (Event.element(e).getAttribute('containerid')) {
						_this.toggle(_this.getControl(Event.element(e).getAttribute('containerid')));
					}
				});
			}
		});

		if (typeof(refreshGUI) == 'function')
			refreshGUI();

		$(this.id).setStyle({ visibility:'visible' });
	},

	getIndex: function(id) {
		for (i=0; i<this.options.tabs.length; i++)
			if (this.options.tabs[i].id == id)
				return i;
		return -1;
	},

	getControl: function(id) {
		var i = this.getIndex(id);
		return (i == -1)?null:this.options.tabs[i];
	},

	addTab: function(tab) {
		if ($('secureHP'))
			$('secureHP').setStyle({ display: 'none' });

		if (this.options.tabs.length >= DF.maxTabs) {
			alert('You cannot open more than ' + DF.maxTabs + ' forms at one time.\nPlease close an existing form and try again.');
			tab = null;
			return false;
		}

		var exists = $A(this.options.tabs).find( function(item){
			return (item.id == tab.id);
		});

		if (!exists) {
			tab.options.tabs = this;
			this.options.tabs.push(tab);
			this.hideControls();
			tab.create();
			this.showControls();
		} else {
			this.hideControls();
			tab = exists;
			tab.options.active = true;
			tab.show();
			this.showControls();
		}

		return tab;
	},

	dropTab: function(id) {
		var idx = this.getIndex(id);
		var tab = this.getControl(id);
		var elm = $(tab.id);

		for (i=idx; i<this.options.tabs.length - 1; i++)
			this.options.tabs[i] = this.options.tabs[i+1];

		this.options.tabs[this.options.tabs.length-1] = null;
		this.options.tabs.length--;

		$(this.options.parentid).removeChild(elm);

		if (tab.options.active && this.options.tabs.length > 0) {
			if (idx == this.options.tabs.length)
				this.toggle(this.options.tabs[idx-1]);
			else
				this.toggle(this.options.tabs[idx]);
		}
		else
			this.showControls();

		tab = null;

		if (this.options.tabs.length == 0 && $('secureHP')) {
			$('secureHP').setStyle({ display:'block' } );
			$('faqLink').href = '/secure/reports/reportengine_faq.php';
		}
	},

	toggle: function(tab) {
		if ($('secureHP')) $('secureHP').setStyle({ display: 'none' });

		this.hideControls();
		tab.show();
		this.showControls();

		if (tab.type == 'panel')
			$(tab.options.form.id+'Canvas').setStyle({ height: tab.options.form.calcHeight() });
	},

	minimize: function() {
		this.hideControls();
		this.showControls();
		$('secureHP').setStyle({ display: 'block' });
		$('faqLink').href = '/secure/reports/reportengine_faq.php';
	}
});

/* ========================================================================== */

DF.Panel = Class.create({
	initialize: function(options) {
		this.setOptions(options);
		this.type = 'panel';
		
		this.hidden = this.options.hidden;
		this.showCallback = this.options.showCallback;
		
		this.id = this.options.id
	},

	setOptions: function(options) {
		this.options = {
			tabs: 		'',
			controls:	new Array(),

			title:		'',
			id:			DF.Utilities.uid(),
			form:		{id:''},

			active:		true,
			hidden:		false,
			
			showCallback: null
		}
		Object.extend(this.options, options || {});
	},

	/*
	TODO
	fGetControlIdx: function(id) {
		for (i=0;i<this.options.vControls.length;i++)
			if (this.options.vControls[i].fGetID() == id)
				return i;
	},
	*/

	create: function() {
		this.id = this.options.form.id+'_'+this.id;
		this.options.active = true;
	},

	hide: function() {
		this.options.active = false;
		$A(this.options.controls).each(function(item) {
			item.visible = false;
			item.update();
		});
	},

	show: function(noCallback) {
		this.options.active = true;
		$A(this.options.controls).each(function(item) {
			item.visible = true;
			item.update();
		});
		
		if (typeof(this.showCallback) == 'function' && typeof(noCallback) == 'undefined')
			this.showCallback();
	},

	addControl: function(ctrl) {
		if (this.options.form.addControl)
			this.options.form.addControl(ctrl);
		this.options.controls.push(ctrl);
		this.options.controls = this.options.controls.uniq();
		
		return ctrl;
	},

	dropControl: function(ctrl) {
		this.options.controls = this.options.controls.without(ctrl);
		if (this.options.form.dropControl)
			this.options.form.dropControl(ctrl);
		else
			ctrl.clear();
			ctrl = null;
	},

	save: function(msg) {
		this.dropMessage();

		msg = (typeof(msg) == 'undefined')?'':msg;
		
		var requiredMsg = '';
		$A(this.options.controls).each(function(item) {
			if (item.type != 'grid' && item.required && ((item.type == 'select' && item.data == -1) || item.value == '')) {
				requiredMsg += '<strong>' + item.label + '</strong> : is a required field, please enter a value.<br>';
			}
		});

		if (!requiredMsg.empty() || !msg.empty()) {
			this.message(requiredMsg + msg);
			return false;
		} else {
			return true;
		}
	},

	message: function(msg, isError) {
		if (typeof(isError) == 'undefined') isError = true;

		var tmp = "<div class=\"messageClose\">Click to close</div>";
		
		$(this.options.form.id+'Messages').className = (isError)?' error':' success';
		$(this.options.form.id+'Messages').innerHTML = msg + tmp;
		$(this.options.form.id+'Messages').style.display = 'block';
		
		var _this = this;
		DF.Utilities.addEvent($(this.options.form.id+'Messages'), 'click', function() {
			$(_this.options.form.id+'Messages').innerHTML = '';
			$(_this.options.form.id+'Messages').style.display = 'none';
		});
	},
	
	dropMessage: function() {
		$(this.options.form.id+'Messages').innerHTML = '';
		$(this.options.form.id+'Messages').style.display = 'none';
	}
});

/* ========================================================================== */

DF.Form = Class.create({
	initialize: function(options) {
		this.setOptions(options);
		this.type = 'form';
		
		this.id = this.options.id
		this.clearCallback = this.options.clearCallback;
		this.loadCallback = this.options.loadCallback;
		this.populateCallback = this.options.populateCallback;
		this.datasetCallback = this.options.datasetCallback;
	},

	setOptions: function(options) {
		this.options = {
			tabs:				'',
			controls:			new Array(),
			datasets:			new Array(),

			title:				'',
			id:					DF.Utilities.s4(),
			formIcons:			'',
			formURL:			'',
			formPostURL:		'',
			formRights:			1,
			
			showLoad:			true,
			showSave:			true,
			showClear:			true,
			showDelete:			true,
			showClose:			true,

			active:				true,
			
			clearCallback:		null,
			loadCallback:		null,
			populateCallback:	null,
			datasetCallback:	null
		}
		Object.extend(this.options, options || {});
	},

	create: function() {
		this.options.active = true;

		var icons = $A(this.options.formIcons);
		this.options.showLoad = (icons[0] == 1)?true:false;
		this.options.showSave = (icons[1] == 1)?true:false;
		this.options.showClear = (icons[2] == 1)?true:false;
		this.options.showDelete = (icons[3] == 1)?true:false;
		this.options.showClose = (icons[4] == 1)?true:false;

		var url = DF.masterFormPath;
		var pars = '' +
			'tabsName=' + this.options.tabs.options.name +
			'&frmID=' + this.id +
			'&frmTitle=' + this.options.title +
			'&frmURL=' + this.options.formURL +
			'&frmPostURL=' + this.options.formPostURL +
			'&frmShowLoad=' + this.options.showLoad +
			'&frmShowSave=' + this.options.showSave +
			'&frmShowClear=' + this.options.showClear +
			'&frmShowDelete=' + this.options.showDelete +
			'&frmShowClose=' + this.options.showClose +
			'&frmRights=' + this.options.formRights;

		var myAjax = new Ajax.Updater(
			this.options.tabs.options.parentid,
			url,
			{
				insertion: Insertion.Bottom,
				method: 'get',
				parameters: pars,
				evalScripts: true,
				onFailure: function(transport) {
					DF.Utilities.error(transport.responseText);
				}
			});
	},

	hide: function() {
		this.options.active = false;
		if ($(this.id))
			$(this.id).setStyle({ display: 'none' });
	},

	show: function() {
		if (typeof(this.datasetCallback) == 'function') {
			this.datasetCallback();

			$(this.id+'Loading').setStyle({ display: 'block' });
			$(this.id+'Canvas').setStyle({ display: 'none' });

			this.options.active = true;
			$(this.id).setStyle({ display: 'block' });

			this.showCanvas();
		} else {
			this.options.active = true;
			$(this.id).setStyle({ display: 'block' });
		}
	},

	showCanvas: function() {
		var _this = this;
		new PeriodicalExecuter(function(pe) {
		// LOOP THROUGH AND CHECK ALL DATASETS AND GRIDS ARE LOADED
			var gloaded = $A(_this.options.controls).all(function(item) {
				if (item.type == 'grid')
					return item.loaded;
				else
					return true;
			});
			
			var dloaded = $A(_this.options.datasets).all(function(item) {
				if (item.type == 'dataset')
					return (item.options.execute && item.loaded); 
				else
					return true;
			});


			if (gloaded && dloaded) {
				pe.stop();

				$(_this.id+'Loading').setStyle({ display: 'none' });
				$(_this.id+'Canvas').setStyle({ display: 'block' });

				$(_this.id+'Canvas').style.height = _this.calcHeight();
			}
		}, .5);
	},

	getIndex: function(id) {
		for (i=0; i<this.options.controls.length; i++)
			if (this.options.controls[i].id == id)
				return i;
		return -1;
	},

	getControl: function(id) {
		var i = this.getIndex(id);
		return (i == -1)?null:this.options.controls[i];
	},

	addControl: function(ctrl) {
		var exists = $A(this.options.controls).find( function(item){
			return (item.id == ctrl.id);
		});

		if (!exists) {
			ctrl.options.form = this;
			
			this.options.controls.push(ctrl);
			if (ctrl.type != 'tabs')
				ctrl.create();
		}

		return ctrl;
	},

	dropControl: function(ctrl) {
		this.options.controls = this.options.controls.without(ctrl);
		ctrl.clear();
		ctrl = null;
	},

	addDataset: function(dset) {
		var exists = $A(this.options.datasets).find( function(item){
			return (item.id == dset.id);
		});

		if (!exists) {
			this.options.datasets.push(dset);
		}

		return dset;
	},

	calcHeight: function() {
		var maxY = 0;

		for (i=0; i<this.options.controls.length; i++) {
			var ctrl = this.options.controls[i];
			if (ctrl.visible && $(ctrl.id).style.display != 'none') {
				var thisY = ctrl.y + (($(ctrl.id))?$(ctrl.id).getHeight():0);
				maxY = (thisY > maxY)?thisY:maxY;
			}
		}
		
		return maxY+'px';
	},

	showSavingBtn: function() {
		if ($$('#'+this.id+' .saveBtn')[0])			
			$$('#'+this.id+' .saveBtn')[0].hide();
		if ($$('#'+this.id+' .lockedBtn')[0])
			$$('#'+this.id+' .lockedBtn')[0].hide();
		if ($$('#'+this.id+' .savingBtn')[0])
			$$('#'+this.id+' .savingBtn')[0].show();
	},

	showSaveBtn: function() {
		if ($$('#'+this.id+' .saveBtn')[0])			
			$$('#'+this.id+' .saveBtn')[0].show();
		if ($$('#'+this.id+' .lockedBtn')[0])
			$$('#'+this.id+' .lockedBtn')[0].hide();
		if ($$('#'+this.id+' .savingBtn')[0])
			$$('#'+this.id+' .savingBtn')[0].hide();
	},
	
	showLockedBtn: function() {
		if ($$('#'+this.id+' .saveBtn')[0])			
			$$('#'+this.id+' .saveBtn')[0].hide();
		if ($$('#'+this.id+' .lockedBtn')[0])
			$$('#'+this.id+' .lockedBtn')[0].show();
		if ($$('#'+this.id+' .savingBtn')[0])
			$$('#'+this.id+' .savingBtn')[0].hide();
	},

	dropMessage: function() {
		$(this.id+"Messages").innerHTML = '';
		$(this.id+"Messages").setStyle({ display: 'none' });
	},

	load: function() {
		this.dropMessage();

		var newDiv1 = new Element('div', { id: 'shade' });
		newDiv1.setStyle({
			width: '100%',
			height: '100%',
			position: 'fixed',
			top: '0px',
			left: '0px',
			zIndex: '100',
			background: '#000000',
			display: 'block',
			opacity: .5,
			filter: 'alpha(opacity=50)'
		});
		$('container').appendChild(newDiv1);

		var newDiv2 = new Element('div', { id: 'load' });
		newDiv2.setStyle({
			width: $(this.id+'Canvas').getWidth()+'px',
			height: $(this.id+'Canvas').getHeight()+'px',
			position: 'absolute',
			top: (Position.cumulativeOffset($(this.id+'Canvas'))[1] - Position.cumulativeOffset($('container'))[1]) + 'px',
			left: (Position.cumulativeOffset($(this.id+'Canvas'))[0] - Position.cumulativeOffset($('container'))[0]) + 'px',
			zIndex: '101',
			display: 'block'
		});
		$('container').appendChild(newDiv2);

		if (typeof(this.loadCallback) == 'function') {
			this.loadCallback();
		}
	},

	save: function(validationMsg, postParams) {
		this.dropMessage();
		this.showSavingBtn();

		validationMsg = (typeof(validationMsg) == 'undefined')?'':validationMsg;
		
		var requiredMsg = '';
		var _this = this;

		$A(this.options.controls).each(function(item) {
			if (item.options.post) {
				var type = item.type;
				if (type != 'grid' && item.required && ((type == 'select' && item.data == -1) || item.value.strip() == '')) {
					requiredMsg += '<strong>' + item.label + '</strong> : is a required field, please enter a value.<br>';
				}
			}
		});

		if (!requiredMsg.empty() || !validationMsg.empty()) {
			var tmp = "<div class=\"messageClose\">Click to close</div>";
			$(this.id+'Messages').className = ' error';
			$(this.id+'Messages').innerHTML = requiredMsg + validationMsg + tmp;
			$(this.id+'Messages').style.display = 'block';
			DF.Utilities.addEvent($(this.id+'Messages'), 'click', function() {
				$(_this.id+"Messages").innerHTML = '';
				$(_this.id+"Messages").style.display = 'none';
			});

			this.showSaveBtn();
		} else {
			var postData = '{';

			$A(this.options.controls).each(function(item, i) {
				if (item.options.post) {
					var type = item.type;
					if (type != 'grid') {
						if (type == 'suggest' || type == 'select' || type == 'multiselect' || type == 'checkbox') {
							var tmp = item.data;							
						} else if (type == 'input' && item.data != '') {
							var tmp = item.data;
						} else {
							var tmp = item.value;
						}

						postData += ((i>0)?',"':'"') + ((item.options.postName != 'undefined')?item.options.postName:item.id) + '":{' +
							'"value":"' + tmp + '",' +
							'"original":"' + item.options.original + '",' +
							'"type":"' + item.type + '",' +
							((typeof(item.options.fieldID) != 'undefined')?'"fieldID":"' + item.options.fieldID + '",' : '') +
							((typeof(item.options.history) != 'undefined')?'"history":"' + item.options.history + '",' : '') +							
							'"rptField":"' + item.options.rptField + '",' +
							'"rptParamType":"' + item.options.rptParamType + '"}';
					} else {
						postData += ((i>0)?',"':'"') + item.options.postName + '":{' +
							'"numrows":"' + item.dataset.records.length + '"';
						if (item.dataset.records.length > 0) {
							postData += ',"records":[';

							$A(item.dataset.records).each(function(record, j) {
								postData += ((j>0)?',':'') + '{';
								$A(item.dataset.config).each(function(column, k) {
									if (record[column.name])
										postData += ((k>0)?',"':'"') + column.name + '":{' +
											'"value":"' + record[column.name].value + '",' + 
											'"original":"' + record[column.name].original + '",' +
											'"type":"' + column.type + '",' +
											'"fieldID":"' + column.cid + '",' +
											'"history":"' + column.history + '"' +
										'}';
								});
								postData += '}';
							});

							postData += ']';
						}
						postData += '}';
					}
				}
			});

			if (typeof(postParams) != 'undefined') {
				postData += ',"extras":'+Object.toJSON(postParams);
			}
			postData += '}';
			
			new Ajax.Request(this.options.formPostURL, {
				method: 'post',
				parameters: { data: postData },
				onSuccess: function(transport) {
					var tmpClose = "<div class=\"messageClose\">Click to close</div>";

					if (transport.responseText.isJSON()) {
						var tmp = transport.responseText.evalJSON();

						if (tmp.status == "success") {
							if (typeof(tmp.pkey) != 'undefined' && typeof(_this.populateCallback) == 'function')
								_this.populateCallback(tmp.pkey);
							else
								_this.clear();
						}

						$(_this.id+'Messages').className = ' ' +tmp.status;
						$(_this.id+'Messages').innerHTML = tmp.message + tmpClose;
					} else {
						$(_this.id+'Messages').className = ' error';
						$(_this.id+'Messages').innerHTML = transport.responseText + tmpClose;
					}
					
					$(_this.id+'Messages').style.display = 'block';
					DF.Utilities.addEvent($(_this.id+'Messages'), 'click', function() {
						$(_this.id+"Messages").innerHTML = '';
						$(_this.id+"Messages").style.display = 'none';
					});

					new PeriodicalExecuter(function(pe) {
						pe.stop();
						_this.showSaveBtn();
					},5);
				},
				onFailure: function(transport) {
					DF.Utilities.error(transport.responseText);

					new PeriodicalExecuter(function(pe) {
						pe.stop();
						_this.showSaveBtn();
					},2.5);
				}
			});
		}
	},

	clear: function() {
		this.dropMessage();

		$(this.id+'Form').reset();

		if (typeof(this.clearCallback) == 'function')
			this.clearCallback();
	},
	
	message: function(msg, isError) {
		if (typeof(isError) == 'undefined') isError = true;

		var tmp = "<div class=\"messageClose\">Click to close</div>";
		
		$(this.id+'Messages').className = (isError)?' error':' success';
		$(this.id+'Messages').innerHTML = msg + tmp;
		$(this.id+'Messages').style.display = 'block';
		
		var _this = this;
		DF.Utilities.addEvent($(this.id+'Messages'), 'click', function() {
			$(_this.id+'Messages').innerHTML = '';
			$(_this.id+'Messages').style.display = 'none';
		});
	},
	
	dropMessage: function() {
		$(this.id+'Messages').innerHTML = '';
		$(this.id+'Messages').style.display = 'none';
	}
});

/* ========================================================================== */

DF.Control = {
	tabIndex: 0,
	maxSuggestRows: 10,
	delaySuggest: 250
}

/* ---------------------------------- */

DF.Control.Base = Class.create({
	initialize: function(options) {
		this.setOptions(options);
		
		this.id = this.options.id;
		this.type = this.options.type;
		this.x = this.options.x;
		this.y = this.options.y;
		this.z = this.options.z;
		this.label = this.options.label;
		this.value = this.options.value;
		this.data = this.options.data;
		this.disabled = this.options.disabled;
		this.required = this.options.required;
		this.visible = this.options.visible;
	},
	
	setOptions: function(options) {
		this.options = {
			type: 			'',
			post: 			true,
			postName: 		'undefined',
			
			x:				0, 
			y:				0, 
			z:				0, 
			right:			false,
			width:			'auto',
			
			id:				DF.Utilities.uid(),
			parentid:		'',
			form:			{id:''},
			
			label:			'',
			labelWidth:		80,
			hint:			'',
			mask:			'',
			calendar:		false,
			
			value:			'',
			data:			'',
			original:		'',
			
			field:			'',
			
			size: 			30,
			maxLength: 		50,
			
			tabIndex: 		++DF.Control.tabIndex,
			
			cssClass:	 	'',
			
			disabled: 		false,
			required: 		false,
			visible: 		true,

			autoSuggest:	false,
			noSelect:		false,

			fieldID:		'',
			history:		false,
			
			rptField: 		'',
			rptParamType: 	'',
			
			dataset:		null,
			loaded:			false,
			
			selectCallback:	null			
		}
		Object.extend(this.options, options || {});
	},
	
	createBase: function() {
		this.id = this.options.form.id + '_' + this.id;
		
		var nContainer = new Element('div', { 
			id: this.id + 'Container' 
		});
		nContainer.setStyle({
			position: 'absolute',
			top: this.y+'px',
			left: ((this.options.right)?'auto':this.x+'px'),
			right: ((this.options.right)?this.x+'px':'auto'),
			zIndex: this.z,
			visibility: ((this.visible)?'visible':'hidden'),
			border:(DF.layouts)?'1px solid red':''
		});

		if (this.type != 'grid') {
			var nLabel = new Element('span', {
				className: 'label ' + ((this.required) ? 'required ' : '')
			});		
			nLabel.setStyle({ 
				width: this.options.labelWidth+'px',
				color: (this.options.parentid == 'load')?'#ffffff':'' 
			});
			nLabel.innerHTML = (this.label != '')?(this.label + "&nbsp;:&nbsp;&nbsp;"):"&nbsp;";
			nContainer.appendChild(nLabel);
		}
		
		return nContainer;
	},

	hint: function(nContainer) {
		if (!this.options.hint.empty()) {
			var nHint = new Element('span', {
				className: 'hint'
			});
			nHint.innerHTML = '&nbsp;&nbsp;' + this.options.hint;
			nContainer.appendChild(nHint);
		}
		return nContainer;
	},

	appendControl: function(nContainer, doObserve) {
		if (this.options.parentid.empty())
			$(this.options.form.id + 'Canvas').appendChild(nContainer);
		else
			$(this.options.parentid).appendChild(nContainer);

		if (typeof(doObserve) == 'undefined' || doObserve) {
			this.observe();
		}
	},
	
	observe: function() {
		if (this.type != 'separator') {
			var _this = this;
			new Form.Element.Observer($(this.id), 0.5, function(element, value) {
				if ($(_this.id))
					_this.sync();
			});
			
			DF.Utilities.addEvent($(this.id), 'keypress', function() {
				return !(window.event && window.event.keyCode == 13);
			});
		}
	},
	
	sync: function() {
		element = $(this.id);
		value = $F(this.id);
		
		if (['text', 'mask', 'button','suggest','multiselect','hidden','password'].include(this.type)) {
			this.value = value;
		} else if (['checkbox'].include(this.type)) {			
			this.value = value;
			this.data = element.checked;				
		} else if (['select'].include(this.type)) {
			this.data = value;

			for (i=0; i<element.options.length; i++) {
				if (element.options[i].selected) {
					this.value = element.options[i].text;	
				}					
			}
		}
	},
	
	updateBase: function(options) {
		if (typeof(options) != 'undefined') {
			var _this = this;
			$H(options).each(function(property) {
				if (typeof(_this[property.key]) != undefined)
					_this[property.key] = property.value;
				else if (typeof(_this.options[property.key]) != undefined)
					_this.options[propery.key] = property.value;
			});
		}
		
		$(this.id).value = this.value;
		$(this.id).disabled = this.disabled;
		$(this.id).className = this.options.cssClass + 
			((['button'].include(this.type))?' frmButton ':'') +
			((['checkbox'].include(this.type))?' noBorder ':'') +
			((this.disabled)?' disabled ':'');
		$(this.id+'Container').setStyle({ visibility: ((this.visible)?'visible':'hidden') });

		_this = this;
		$$('#'+this.id+'Container span.label').each(function(element) {
			element.className = 'label '+((_this.required)?' required ':'');
		});
	},
	
	clear: function(clearOriginal) {
		this.value = '';
		this.data = '';
		if (typeof(clearOriginal) != 'undefined') {
			this.options.original = '';
		}
		
		if (this.type == 'select') {
			for (var i=0;i<$(this.id).options.length;i++) {
				$(this.id).remove(i);
			}
			$(this.id).options.length = 0;
		}
		
		if (this.type == 'multiselect') {
			var total = $(this.id+'LeftBox').options.length;
			for (var i=0;i<total;i++) {
				$(this.id+'LeftBox').remove(i);
			}
			$(this.id+'LeftBox').options.length = 0;
			
			var total = $(this.id+'RightBox').options.length;
			for (var i=0;i<total;i++) {
				$(this.id+'RightBox').remove(i);
			}
			$(this.id+'RightBox').options.length = 0;
		}
	},
	
	callback: function(eventType, functionName) {
		DF.Utilities.addEvent($(this.id), eventType, functionName);	
	}
});

/* ---------------------------------- */

DF.Control.Input = Class.create(DF.Control.Base, {
	initialize: function($super, options) {
    	$super(options);
	},
	
	createInput: function() {
		var nContainer = this.createBase();

		var nControl = new Element('input', {
			id: this.id,
			type: (this.type == 'suggest')?'text':this.type,
			value: this.value,
			size: this.options.size,
			maxlength: this.options.maxLength,			
			tabindex: this.options.tabIndex,
			disabled: this.disabled,
			className: this.options.cssClass+' '+((this.disabled)?'disabled ':'') + ((['checkbox'].include(this.type))?' noBorder ':'') + ((['button'].include(this.type))?' frmButton ':'')
		});		
		nContainer.appendChild(nControl);

		nContainer = this.hint(nContainer);
		
		return nContainer;
	}
});

/* ---------------------------------- */
	
DF.Control.Text = Class.create(DF.Control.Input, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'text';
	},
	
	create: function() {
		this.appendControl(this.createInput());

		if (this.options.calendar) {
			Calendar.setup({
				inputField : this.id,
				ifFormat   : "%m/%d/%Y %l:%M %P",
				button     : this.id,
				eventName  : "focus",
				showsTime  : true
			});
		}
	},
	
	update: function(options) {
		this.updateBase(options);
		
		if (typeof(options) != 'undefined' && typeof(options.setOriginal) != 'undefined' && options.setOriginal)
			this.options.original = this.value;
	}
});

/* ---------------------------------- */
	
DF.Control.Hidden = Class.create(DF.Control.Input, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'hidden';
	},
	
	create: function() {
		this.appendControl(this.createInput());
		$(this.id).type = 'hidden';
	},
	
	update: function(options) {
		this.updateBase(options);
		
		if (typeof(options) != 'undefined' && typeof(options.setOriginal) != 'undefined' && options.setOriginal)
			this.options.original = this.value;
	}
});

/* ---------------------------------- */
	
DF.Control.Password = Class.create(DF.Control.Input, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'password';
	},
	
	create: function() {
		this.appendControl(this.createInput());
		$(this.id).type = 'password';
	},
	
	update: function(options) {
		this.updateBase(options);
		
		if (typeof(options) != 'undefined' && typeof(options.setOriginal) != 'undefined' && options.setOriginal)
			this.options.original = this.value;
	}
});

/* ---------------------------------- */

DF.Control.Mask = Class.create(DF.Control.Input, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'mask'
	},
	
	create: function() {
		this.appendControl(this.createInput());
		
		var _this = this;
		if (!this.options.mask.empty())
			DF.Utilities.addEvent($(this.id), 'keyup', function(e){ 
				_this.doMaskNow(Event.element(e).value);
			});
	},
	
	update: function(options) {
		this.updateBase(options);

		if (typeof(options) != 'undefined' && typeof(options.setOriginal) != 'undefined' && options.setOriginal)
			this.options.original = this.value;
			
		if (!this.options.mask.empty())
			this.doMaskNow(this.value);
	},

	doMaskNow: function(val) {
		val = String(val);
		var ltor = true;
		var mask = this.options.mask;
		if (mask.endsWith('R')) ltor = false;

		mask = mask.toArray();
		mask.length--;

		val = val.replace(/[^0-9]/gi, "");
		val = val.replace(/^0*/gi, "");
		val = val.toArray();

		this.value = '';
		var idx = 0;
		if (!ltor) {
			mask.reverse();
			val.reverse();
		}

		for (var i=0; i<mask.length; i++) {
			if (idx < val.length)
				var append = (mask[i] != '_' && mask[i] != '^') ? mask[i] : val[idx++];
			else
				var append = (mask[i] == '^') ? 0 : (mask[i] != '_') ? mask[i] : '';
			
			this.value += append;
		}

		if (!ltor) {
			var tmp = '';
  			for (var i=0; i<this.value.length; i++)
    			tmp = this.value.charAt(i) + tmp;

			this.value = tmp;
		}	
		
		$(this.id).value = this.value;	
	}
});

/* ---------------------------------- */

DF.Control.Button = Class.create(DF.Control.Input, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'button'
		this.options.post = false;
	},
	
	create: function() {
		this.appendControl(this.createInput());
	},
	
	update: function(options) {
		this.updateBase(options);
	}
});

/* ---------------------------------- */

DF.Control.Checkbox = Class.create(DF.Control.Input, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'checkbox'
		this.data = false;
	},
	
	create: function() {
		this.appendControl(this.createInput());
	},
	
	update: function(options) {
		this.updateBase(options);
		
		if (typeof(options) != 'undefined' && typeof(options.setOriginal) != 'undefined' && options.setOriginal)
			this.options.original = this.data;
		
		$(this.id).checked = this.data;
	}
});

/* ---------------------------------- */

DF.Control.Suggest = Class.create(DF.Control.Input, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'suggest'
		
		this.dataset = this.options.dataset;
		this.selectCallback = this.options.selectCallback;
		
		this.results = new Array();
		this.widths = new Array();
		this.selected = 0;
		this.offset = 0;
		this.delay;
	},
	
	create: function() {
		this.appendControl(this.createInput());
		
		$(this.id).className += ' suggest ';

		var nContainer = new Element('div', {
			id: this.id+'Suggest',
			className: 'suggestBox'
		});
		nContainer.setStyle({
			position: 'absolute',
			top: (this.y+22)+'px',
			left: (this.x+this.options.labelWidth)+'px',
			zIndex: this.z+500
		});

		this.appendControl(nContainer, false);


		var _this = this;
		DF.Utilities.addEvent($(this.id+'Suggest'), 'mousewheel', function(e) { _this.mouse(e); });
		DF.Utilities.addEvent($(this.id), 'click', function(e) { _this.autoRun(); });
		DF.Utilities.addEvent($(this.id), 'keyup', function(e){ _this.key(e); });
		DF.Utilities.addEvent($(this.id), 'keydown', function(e){ _this.key(e); });
		DF.Utilities.addEvent($(this.id), 'blur', function(e){ 
			clearTimeout(_this.delay);
			_this.delay = setTimeout(function(){
				_this.dropSuggest(); 
			},DF.Control.delaySuggest*2);
		});
	},
	
	setIfExists: function(pkey){
		var _this = this;
		this.dataset.records.each(function(record){
			if (record[0].value == pkey){
				_this.update({ data:pkey, value:record[1].value });
			}
		});
	},
	
	update: function(options) {
		this.updateBase(options);
		
		if (this.value.empty())
			this.data = '';

		if (typeof(options) != 'undefined' && typeof(options.setOriginal) != 'undefined' && options.setOriginal)
			this.options.original = this.data;
			
		$(this.id).className += ' suggest ';
	},
	
	mouse: function(e) {
		clearTimeout(this.delay);

		var updateResults = true;
		var delta = wheelDelta(e);
		
		var speed = Math.floor(Math.abs(delta)/5)+1;
		speed = (speed>5)?5:speed;

		if (delta > 0) {
			this.selected -= speed;
				this.selected = (this.selected < 0)?0:this.selected;
			if (this.selected < this.offset) {
				if ((this.offset-DF.Control.maxSuggestRows) >= 0) 
					this.offset -= DF.Control.maxSuggestRows;
				this.selected = this.offset + DF.Control.maxSuggestRows - 1;
			}
		} else if (delta < 0) {
			this.selected += speed;
				this.selected = (this.selected > (this.results.length - 1))?(this.results.length - 1):this.selected;
			if (this.selected > (this.offset+DF.Control.maxSuggestRows - 1)) {
				if ((this.offset+DF.Control.maxSuggestRows) < this.results.length) 
					this.offset += DF.Control.maxSuggestRows;
				this.selected = this.offset;
			}
		} else {
			updateResults = false;
		}

		if (updateResults) {
			this.refresh();
		}
	},

	key: function(e) {
		var keycode = (window.event)?window.event.keyCode:e.which;

		clearTimeout(this.delay);
		
		var updateResults = true;
		if (e.type == 'keydown') {
			switch(keycode) {
				case 33: // PAGE UP
					if ((this.offset-DF.Control.maxSuggestRows) >= 0) this.offset -= DF.Control.maxSuggestRows;
					this.selected = this.offset;
					break;
				case 34: // PAGE DOWN
					if ((this.offset+DF.Control.maxSuggestRows) < this.results.length) this.offset += DF.Control.maxSuggestRows;
					this.selected = this.offset;
					break;
				case 38: // UP ARROW
					this.selected--;
	  				this.selected = (this.selected < 0)?0:this.selected;
					if (this.selected < this.offset) {
						if ((this.offset-DF.Control.maxSuggestRows) >= 0) this.offset -= DF.Control.maxSuggestRows;
						this.selected = this.offset + DF.Control.maxSuggestRows - 1;
					}
	  				break;
				case 40: // DOWN ARROW
					this.selected++;
	  				this.selected = (this.selected > (this.results.length - 1))?(this.results.length - 1):this.selected;
					if (this.selected > (this.offset+DF.Control.maxSuggestRows - 1)) {
						if ((this.offset+DF.Control.maxSuggestRows) < this.results.length) this.offset += DF.Control.maxSuggestRows;
						this.selected = this.offset;
					}
					break;
				case 13: // ENTER
					this.data = this.results[this.selected][0].value;
					this.value = this.results[this.selected][1].value;
					var guid = (this.results[this.selected].tpkey)?this.results[this.selected].tpkey.value:0;
					this.update();
					this.dropSuggest();

					if (this.selectCallback != null) {
						var _this = this;
						setTimeout(function(){ _this.selectCallback(_this.selected, guid) }, 250);
					}
					break;
				case 27: // ESC
					this.data = '';
					this.dropSuggest();
					break;
				default:
					updateResults = false;
			}

			if (updateResults)
				this.refresh();
		}

		if (e.type == 'keyup') {
			switch(keycode) {
				case 9: break;
				case 13: break;
				case 27: break;
				case 33: break;
				case 34: break;
				case 38: break;
				case 40: break;
				default:
					updateResults = false;
					this.data = '';
					var _this = this;
					this.delay = setTimeout(function(){ 
						_this.suggest(); 
					}, DF.Control.delaySuggest);
			}
		}

		if (updateResults)
			DF.Utilities.resetCaret($(this.id), $(this.id).value.length);
	},
	
	pageUp: function() {
		clearTimeout(this.delay);
		if ((this.offset-DF.Control.maxSuggestRows) >= 0) this.offset -= DF.Control.maxSuggestRows;
		this.selected = this.offset;
		this.refresh();

		$(this.id).focus();
		DF.Utilities.resetCaret($(this.id), $(this.id).value.length);
	},

	pageDown: function() {
		clearTimeout(this.delay);
		if ((this.offset+DF.Control.maxSuggestRows) < this.results.length) this.offset += DF.Control.maxSuggestRows;
		this.selected = this.offset;
		this.refresh();

		$(this.id).focus();
		DF.Utilities.resetCaret($(this.id), $(this.id).value.length);
	},
	
	autoRun: function() {
		$(this.id).focus();
		this.clear();
		clearTimeout(this.delay);
		var _this = this;
		this.delay = setTimeout(function(){ 
			_this.suggest(); 
		}, DF.Control.delaySuggest*2);
	},

	suggest: function() {
		this.results.length = 0;
		this.selected = this.offset = 0;

		var re = new RegExp("(\\b)+"+$(this.id).value,"i");
		if (this.dataset.loaded) {
			var _this = this;
			this.results = this.dataset.records.findAll(function(record) {
				for (var i=0; i<record.length; i++)
					if (_this.dataset.config[i].search == 'true')
						if (re.test(record[i].value)) 
							return true; 
				return false;
			});

			// FIND MAX STRING FOR EACH VISIBLE COLUMN AND SET THE WIDTH
			for (i=0; i<this.dataset.config.length; i++)
				this.widths[i] = (this.results.max(function(record) { return record[i].value.length; })*6)+'px';
		}
		this.refresh();
	},

	refresh: function() {
		if($(this.id+'Suggest')) {
			if (this.results.length > 0) {
				$(this.id+'Suggest').innerHTML = '';
				$(this.id+'Suggest').appendChild(this.displayResults(this.results));
				$(this.id+'Suggest').setStyle({ display:'block' });
	
				// REPOSITION THE SUGGEST BOX TO KEEP IT ON THE SCREEN, SAVE FOR FUTURE
				if (this.options.parentid.empty()) {
					if (($(this.id+'Suggest').getWidth() + this.x + this.options.labelWidth) > $(this.options.form.id).getWidth())
						$(this.id+'Suggest').setStyle({ left:($(this.options.form.id).getWidth() - $(this.id+'Suggest').getWidth()) + 'px' });
					else
						$(this.id+'Suggest').setStyle({ left:(this.x + this.options.labelWidth) + 'px' });
				} else {
					if (($(this.id+'Suggest').getWidth() + this.x + this.options.labelWidth) > $(this.options.parentid).getWidth())
						$(this.id+'Suggest').setStyle({ left:($(this.options.parentid).getWidth() - $(this.id+'Suggest').getWidth()) + 'px' });
					else
						$(this.id+'Suggest').setStyle({ left:(this.x + this.options.labelWidth) + 'px' });
				}
			} else {
				$(this.id+'Suggest').setStyle({ display: 'none' });
				$(this.id+'Suggest').innerHTML = '';
			}
		}
	},

	displayResults: function(tmpArray) {
		var keyword = $(this.id).value;

		var maxRows = ((tmpArray.length-this.offset)>DF.Control.maxSuggestRows)?DF.Control.maxSuggestRows:(tmpArray.length-this.offset);

		var nTable = new Element('table', { cellSpacing: 0 });
		var nTBody = new Element('tbody');
		nTable.appendChild(nTBody);

		var nTR = new Element('tr');
		nTBody.appendChild(nTR);

		for (i=0; i<this.dataset.config.length; i++)
			if (this.dataset.config[i].sshow === 'true') {
				var nTD = new Element('td', { className:'title' });
				nTD.setStyle({ width:this.widths[i] });
				nTD.innerHTML = this.dataset.config[i].title.replace(' ','&nbsp;');
				nTR.appendChild(nTD);
			}

		for (i=this.offset; i<(this.offset+maxRows); i++) {
			var nTR = new Element('tr', {
				className:((i == this.selected)?'selected ':'')+(((i%2) == 1)?'alt ':''),
				originalClassName:((i == this.selected)?'selected ':'')+(((i%2) == 1)?'alt ':''),
				datasetIndex:i
			});

			for (j=0; j<tmpArray[i].length; j++)
				if (this.dataset.config[j].sshow == 'true') {
					var field = tmpArray[i][j].value;
					var nTD = new Element('td');
					nTD.innerHTML = (field == '')?'&nbsp;':field;
					nTR.appendChild(nTD);
				}

			nTBody.appendChild(nTR);

			_this = this;
			DF.Utilities.addEvent(nTR, 'mouseover', function(e){
				var tmp = Event.element(e).parentNode.getAttribute('datasetIndex');
				if (tmp != _this.selected) {
					_this.selected = tmp;
					_this.refresh();
				}
			});
			DF.Utilities.addEvent(nTR, 'click', function(e){
				_this.data = _this.results[_this.selected][0].value;
				_this.value = _this.results[_this.selected][(_this.options.field.empty())?1:_this.options.field].value;
				var guid = (_this.results[_this.selected].tpkey)?_this.results[_this.selected].tpkey.value:0;
				_this.update();

				if (_this.selectCallback != null) {
					setTimeout(function(){ _this.selectCallback(_this.selected, guid) }, 250);
				}
			});
		}

		var nTR = new Element('tr');
		var nTD = new Element('td', {
			colSpan: tmpArray[0].length,
			className: 'info'
		});

		var nDiv = new Element('div');
		nDiv.setStyle({
			width:'100%',
			position:'relative'
		}).update('Showing <strong>'+(this.offset+1)+' - '+(this.offset+maxRows)+'</strong> of <strong>'+tmpArray.length+'</strong> results');
		
		if (this.offset > 0) {
			var nA1 = new Element('a', {
				href: 'javascript://',
				className: 'info'
			}).update('&lt; Prev Page');
			nA1.setStyle({
				position:'absolute',
				left:'5px',
				bottom:'1px'
			});
			var _this = this;
			DF.Utilities.addEvent(nA1, 'click', function(e) { _this.pageUp(); });
			nDiv.appendChild(nA1);
		}

		if (this.offset+maxRows < tmpArray.length) {
			var nA2 = new Element('a', {
				href: 'javascript://',
				className: 'info'
			}).update('Next Page &gt;');
			nA2.setStyle({
				position:'absolute',
				right:'5px',
				bottom:'1px'
			});
			var _this = this;
			DF.Utilities.addEvent(nA2, 'click', function(e) { _this.pageDown(); });
			nDiv.appendChild(nA2);
		}

		nTD.appendChild(nDiv);
		nTR.appendChild(nTD);
		nTBody.appendChild(nTR);

		return nTable;
	},
	
	dropSuggest: function() {
		if (this.data == '' && $(this.id))
			$(this.id).value = '';

		this.results.length = 0;
		this.refresh();
	}
});

/* ---------------------------------- */

DF.Control.Select = Class.create(DF.Control.Base, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'select'
		
		if (!options.size)
			this.options.size = 1;
			
		this.dataset = this.options.dataset;
	},
	
	create: function() {
		var nContainer = this.createBase();

		var nControl = new Element('select', {
			id: this.id,
			size: this.options.size,
			tabindex: this.options.tabIndex,
			disabled: this.disabled,
			className: this.options.cssClass+' '+((this.disabled)?'disabled ':'')
		});		
		nContainer.appendChild(nControl);
		
		nContainer = this.hint(nContainer);
		
		this.appendControl(nContainer);
		
		if (this.dataset != null) {
			this.dataset.addControl(this);
		}
	},
	
	update: function(options) {
		this.updateBase(options);
		
		if (typeof(options) != 'undefined' && typeof(options.data) != 'undefined')
			for (i=0;i<$(this.id).options.length; i++) {
				if ($(this.id).options[i].value == options.data) {
					$(this.id).selectedIndex = i;
					break;
				}
			}
		else if (typeof(options) != 'undefined' && typeof(options.value) != 'undefined')
			for (i=0;i<$(this.id).options.length; i++) {
				if ($(this.id).options[i].text == options.value) {
					$(this.id).selectedIndex = i;
					break;
				}
			}
		else
			for (i=0;i<$(this.id).options.length; i++) {
				if ($(this.id).options[i].value == this.data || $(this.id).options[i].text == this.value) {
					$(this.id).selectedIndex = i;
					break;
				}
			}
		
		this.sync();
		if (typeof(options) != 'undefined' && typeof(options.setOriginal) != 'undefined' && options.setOriginal)
			this.options.original = this.data;
	},
	
	populate: function() {
		if (this.dataset != null) {
			this.clear();
		
			if (!this.options.noSelect) {
				var nOption = document.createElement('option');
				nOption.value = -1;
				nOption.text = 'Select';
				
				try {
					$(this.id).add(nOption, null);
				} catch(ex) {
					$(this.id).add(nOption);
				}
			}
			
			for (i=0; i<this.dataset.records.length; i++) {
				var nOption = document.createElement('option');
				nOption.value = this.dataset.records[i][0].value;
				nOption.text = this.dataset.records[i][(this.options.field.empty())?1:this.options.field].value;
				
				if (this.dataset.records.length == 1) {
					nOption.selected = true;
					this.value = nOption.text;
					this.data = nOption.value;
				}

				try {
					$(this.id).add(nOption, null);
				} catch(ex) {
					$(this.id).add(nOption);
				}
	  		}
		}
	},
	
	reset: function() {
		$(this.id).selectedIndex = 0;
	}
});

/* ---------------------------------- */

DF.Control.MultiSelect = Class.create(DF.Control.Input, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'multiselect';
		
		this.availableDataset = this.options.dataset;
		this.selectedDataset = new DF.Dataset();
		
		this.selectCallback = this.options.selectCallback;
	},
	
	create: function() {
		this.appendControl(this.createInput());
		
		$(this.id).className += ' multiSelect ';

		var nShade = new Element('div', { 
			id: this.id+'MultiSelectShade',
			className:'multiSelectShade'			 
		});
		nShade.setStyle({
			display: 'none'
		});
		$(this.options.form.id+'Canvas').appendChild(nShade);

		var nContainer = new Element('div', {
			id: this.id+'MultiSelect',
			className: 'multiSelectBox'
		});
		nContainer.setStyle({
			position: 'absolute',
			top: (this.y+22)+'px',
			left: (this.x+this.options.labelWidth)+'px',			
			zIndex: this.z+500
		});
		
		var nLeftBox = new Element('select', {
			id:	this.id+'LeftBox',
			size: 10,
			multiple: 'multiple',
			className: 'multiSelectLeft'
		});
		nContainer.appendChild(nLeftBox);

		var nRightBox = new Element('select', {
			id:	this.id+'RightBox',
			size: 10,
			multiple: 'multiple',
			className: 'multiSelectRight'
		});
		nContainer.appendChild(nRightBox);
		
		
		var _this = this;
		
		var nPushAllRight = new Element('a', {
			id:	this.id+'PushAllRight',
			href: 'javascript://',
			className: 'multiSelectPushAllRight'
		}).update('>>');		
		DF.Utilities.addEvent(nPushAllRight,'click',function(e) { _this.push('allRight'); });
		nContainer.appendChild(nPushAllRight);
		 
		var nPushRight = new Element('a', {
			id:	this.id+'PushRight',
			href: 'javascript://',
			className: 'multiSelectPushRight'
		}).update('>');
		DF.Utilities.addEvent(nPushRight,'click',function(e) { _this.push('right'); });
		nContainer.appendChild(nPushRight);
		
		var nPushLeft = new Element('a', {
			id:	this.id+'PushLeft',
			href: 'javascript://',
			className: 'multiSelectPushLeft'
		}).update('<');
		DF.Utilities.addEvent(nPushLeft,'click',function(e) { _this.push('left'); });
		nContainer.appendChild(nPushLeft);
		
		var nPushAllLeft = new Element('a', {
			id:	this.id+'PushAllLeft',
			href: 'javascript://',
			className: 'multiSelectPushAllLeft'
		}).update('<<');
		DF.Utilities.addEvent(nPushAllLeft,'click',function(e) { _this.push('allLeft'); });
		nContainer.appendChild(nPushAllLeft);

		var nClose = new Element('input', {
			id:	this.id+'Close',
			type: 'button',
			value: 'Close',
			className: 'multiSelectClose frmButton'
		});
		DF.Utilities.addEvent(nClose,'click',function(e) { 
			$(_this.id+'MultiSelectShade').hide();
			$(_this.id+'MultiSelect').style.display = 'none'; 
		});
		nContainer.appendChild(nClose);

		this.appendControl(nContainer, false);

		DF.Utilities.addEvent($(this.id), 'focus', function(e) { 
			_this.populate(true);
			$(_this.id+'MultiSelectShade').show();
			$(_this.id+'MultiSelect').style.display = 'block';
		});
	},

	push: function(type) {
		if (type == 'allRight') {
			this.pushAllRight();
		} else if (type == 'right') {
			this.pushRight();
		} else if (type == 'left') {
			this.pushLeft();
		} else if (type == 'allLeft') {
			this.pushAllLeft();
		}
		this.sortDatasets();

		this.clear();
		this.update();		
		this.populate();
	},
	pushAllRight: function() {
		this.selectedDataset.records = this.selectedDataset.records.concat(this.availableDataset.records);
		this.availableDataset.clear();
	},
	pushRight: function() {
		for (var i=0; i<$(this.id+'LeftBox').options.length; i++) {
			if ($(this.id+'LeftBox').options[i].selected) {
				var record = this.availableDataset.getRecord($(this.id+'LeftBox').options[i].value);
				this.availableDataset.removeRecord(record);
				this.selectedDataset.records.push(record);
			}
		}		
	},
	pushLeft: function() {
		for (var i=0; i<$(this.id+'RightBox').options.length; i++) {
			if ($(this.id+'RightBox').options[i].selected) {
				var record = this.selectedDataset.getRecord($(this.id+'RightBox').options[i].value);
				this.selectedDataset.removeRecord(record);
				this.availableDataset.records.push(record);
			}
		}		
	},
	pushAllLeft: function() {
		this.availableDataset.records = this.availableDataset.records.concat(this.selectedDataset.records);
		this.selectedDataset.clear();
	},
	sortDatasets: function() {
		var _this = this;
		this.availableDataset.records = $A(this.availableDataset.records).sortBy(function(record) {
			return record[(_this.options.field.empty())?1:_this.options.field].value;
		}) ;						
		this.selectedDataset.records = $A(this.selectedDataset.records).sortBy(function(record) {
			return record[(_this.options.field.empty())?1:_this.options.field].value;
		}) ;							
	},
	
	setIfExists: function(pkey){
		var _this = this;
		this.availableDataset.records.each(function(record){
			if (record[0].value == pkey){
				var tmpData = _this.data+((_this.data.empty())?"":",")+pkey;
				var tmpValue = _this.value+((_this.value.empty())?"":"; ")+record[(_this.options.field.empty())?1:_this.options.field].value;
				_this.update({ data:tmpData, value:tmpValue });
			}
		});
	},

	populate: function(initialLoad) {
		if (typeof(initialLoad) == 'undefined')
			initialLoad = false;
		
		if (this.availableDataset != null && this.selectedDataset != null) {
			if (initialLoad) {
				this.pushAllLeft();
				var _this = this;
				this.data.scan(/\d+/, function(pkey) {
					var record = _this.availableDataset.getRecord(pkey);
					if (typeof(record) != 'undefined') {
						_this.availableDataset.removeRecord(record);
						_this.selectedDataset.records.push(record);
					}
				});
				this.sortDatasets();
				this.clear();
			}

			for (var i=0; i<this.availableDataset.records.length; i++) {
				var nOption = document.createElement('option');
				nOption.value = this.availableDataset.records[i][0].value;
				nOption.text = this.availableDataset.records[i][(this.options.field.empty())?1:this.options.field].value;
				
				try {
					$(this.id+'LeftBox').add(nOption, null);
				} catch(ex) {
					$(this.id+'LeftBox').add(nOption);
				}
	  		}
			for (var i=0; i<this.selectedDataset.records.length; i++) {
				var nOption = document.createElement('option');
				nOption.value = this.selectedDataset.records[i][0].value;
				nOption.text = this.selectedDataset.records[i][(this.options.field.empty())?1:this.options.field].value;
				
				try {
					$(this.id+'RightBox').add(nOption, null);
				} catch(ex) {
					$(this.id+'RightBox').add(nOption);
				}
	  		}
			
			for (var i=0; i<$(this.id+'RightBox').options.length; i++) {					
				this.value += ((!this.value.empty())?'; ':'')+$(this.id+'RightBox').options[i].text;
				this.data += ((!this.data.empty())?',':'')+$(this.id+'RightBox').options[i].value;
			}
			this.update();
		}
	},

	update: function(options) {
		this.updateBase(options);
		
		if (this.value.empty())
			this.data = '';

		if (typeof(options) != 'undefined' && typeof(options.setOriginal) != 'undefined' && options.setOriginal)
			this.options.original = this.data;
		
		if (typeof(options) != 'undefined' && typeof(options.reset) != 'undefined' && options.reset)
			this.pushAllLeft();
			
		$(this.id).className += ' multiSelect ';
	}
});

/* ---------------------------------- */

DF.Control.Separator = Class.create(DF.Control.Base, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'separator'
		this.options.post = false;
	},
	
	create: function() {
		var nContainer = this.createBase();
		nContainer.setStyle({ 'width':this.options.width });
		
		var nControl = new Element('hr', {
			id: this.id 
		});
		nContainer.appendChild(nControl);
		
		this.appendControl(nContainer);
	},
	
	update: function(options) {
		if (typeof(options) != 'undefined') {
			var _this = this;
			$H(options).each(function(property) {
				if (typeof(_this[property.key]) != undefined)
					_this[property.key] = property.value;
				else if (typeof(_this.options[property.key]) != undefined)
					_this.options[propery.key] = property.value;
			});
		}
		
		$(this.id+'Container').setStyle({ visibility: ((this.visible)?'visible':'hidden' )});
	}
});

/* ---------------------------------- */

DF.Control.Grid = Class.create(DF.Control.Base, {
	initialize: function($super, options) {
    	$super(options);
		this.type = 'grid'
		
		this.dataset = this.options.dataset;
		this.loaded = this.options.loaded;
		this.selectCallback = this.options.selectCallback;
	},

	create: function() {
		var nContainer = this.createBase();
		nContainer.writeAttribute('id',this.id);
		nContainer.className = 'gridBox';		

		this.appendControl(nContainer, false);

		if (this.dataset != null)
			this.dataset.addControl(this);
	},

	update: function(options) {
		if (typeof(options) != 'undefined') {
			var _this = this;
			$H(options).each(function(property) {
				if (typeof(_this[property.key]) != undefined)
					_this[property.key] = property.value;
				else if (typeof(_this.options[property.key]) != undefined)
					_this.options[propery.key] = property.value;
			});
		}
		
		$(this.id).setStyle({ visibility: (this.visible)?'visible':'hidden' });
	},
	
	populate: function() {
		this.loaded = false;

		if ($(this.id+'Table'))
			$(this.id+'Table').remove();

		var nTable = new Element('table', {
			id: this.id+'Table',
			cellSpacing: 0
		});
		var nTBody = new Element('tbody');
		nTable.appendChild(nTBody);
		
		var nTR = new Element('tr');
		nTBody.appendChild(nTR);
		
		for (i=0; i<this.dataset.config.length; i++) {
			if (this.dataset.config[i].gshow == 'true' || DF.debug) {
				var nTD = new Element('td', {
					className: 'title'
				}).update((this.dataset.config[i].title.empty())?this.dataset.config[i].name:this.dataset.config[i].title.replace(' ','&nbsp;'));
				if (this.dataset.config[i].gshow != 'true')
					nTD.setStyle({ background:'#cc0000', color:'#ffffff', fontStyle:'italic' });				
				nTR.appendChild(nTD);
			}
		}
				
		for (i=0; i<this.dataset.records.length; i++) {
			var nTR = new Element('tr', {
				className:(((i%2) == 1)?'alt ':''),
				originalClassName:(((i%2) == 1)?'alt ':''),
				datasetIndex:i
			});
			var isActive = true;
			for (j=0; j<this.dataset.config.length; j++) {
				if (this.dataset.records[i]['status'] && this.dataset.records[i]['status'].value == 2)
					isActive = false;
				else
					if (this.dataset.config[j].gshow == 'true' || DF.debug) {
						var name = this.dataset.config[j].name;
						if (typeof(this.dataset.records[i][name]) == 'undefined')
							var nTD = new Element('td').update('-');
						else if (typeof(this.dataset.records[i][name].value) == 'string' && this.dataset.records[i][name].value.strip().empty())
							var nTD = new Element('td').update('-'+((DF.debug)?'<br>('+this.dataset.records[i][name].original+')':''));
						else
							var nTD = new Element('td').update(this.dataset.records[i][name].value+((DF.debug)?'<br>('+this.dataset.records[i][name].original+')':''));
							
						nTR.appendChild(nTD);
					}
			}

			if (isActive) {
				nTBody.appendChild(nTR);
				DF.Utilities.addEvent(nTR, 'mouseover', function(e){ Event.element(e).parentNode.className = 'selected'; });
				DF.Utilities.addEvent(nTR, 'mouseout', function(e){ Event.element(e).parentNode.className = Event.element(e).parentNode.readAttribute('originalClassName') });
				var _this = this;
				DF.Utilities.addEvent(nTR, 'click', function(e){
					if (typeof(_this.selectCallback) == 'function') {
						_this.selectCallback(Event.element(e).parentNode.readAttribute('datasetIndex'));
					}
				});
			}
			if (i>=99) {
				DF.Utilities.error('Grid records exceed capacity - only displaying first 100 records');
				break;
			}
		}

 		$(this.id).appendChild(nTable);
		
		if (this.options.form.calcHeight)
			$(this.options.form.id+'Canvas').setStyle({ height: this.options.form.calcHeight() });

		this.loaded = true;
	}
});

/* ========================================================================== */

DF.Dataset = Class.create({
	initialize: function(options) {
		this.setOptions(options);
		this.type = 'dataset';
		
		this.id = this.options.id;
		
		this.queryparams = this.options.queryparams;
		this.originalQueryparams = this.options.queryparams;
		this.extrafilter = this.options.extrafilter;
		this.originalExtrafilter = this.options.extrafilter;
		
		this.records = this.options.records;
		this.config = this.options.config;
		this.loaded = this.options.loaded;
		
		if (this.options.execute && !this.options.queryname.empty()) 
			this.exec();
		else
			this.loaded = true;
	},
	
	setOptions: function(options) {
		this.options = {
			records: 		new Array(),
			config: 		new Array(),
			
			controls: 		new Array(),
			callbacks: 		new Array(),
			
			id:				DF.Utilities.uid(),
				
			queryname: 		'',
			queryparams: 	'',
			extrafilter: 	'',
			configonly:		false,
			
			loaded: 		false,
			execute: 		true	
		}
		Object.extend(this.options, options || {});
	},
	
	lookup: function(pkey) {
		return $A(this.records).find( function(record) {
			return (record.pkey.value == pkey);
		});
	},

	addControl: function(ctrl) {
		var exists = $A(this.options.controls).find( function(item){
			return (item.id == ctrl.id);
		});

		if (!exists) {
			ctrl.dataset = this;
			this.options.controls.push(ctrl);
			
			if (this.loaded) {
				ctrl.populate();
			}
		}
	},

	getRecord: function(pkey) {
		return $A(this.records).find(function(item) {
			return (item[0].value == pkey);
		});
	},
	
	removeRecord: function(record) {
		this.records = this.records.without(record);
	},

	addCallback: function(fun) {
		var exists = $A(this.options.callbacks).find( function(item){
			return (item == fun);
		});

		if (!exists) {
			this.options.callbacks.push(fun);
			if (this.loaded)
				fun();
		}
	},

	dropCallback: function(fun) {
		this.options.callbacks = this.options.callbacks.without(fun);
	},

	addRecord: function(record) {
		var _this = this;
		var tmp = new Array();
		$H(record).each(function(property) {
			tmp[property.key] = { value:property.value, original:'' };
		});

		this.records.push(tmp);		
	},
	
	updateRecord: function(i, record) {
		var _this = this;
		$H(record).each(function(property) {
			if (typeof(_this.records[i][property.key]) != undefined)
				_this.records[i][property.key].value = property.value;
		});
	},

	clear: function() {
		$A(this.config).each(function(item) { item = null; });
		this.config.length = 0;
		$A(this.records).each(function(item) { item = null; });
		this.records.length = 0;
	},
	
	reset: function() {
		this.queryparams = this.originalQueryparams;
		this.extrafilter = this.originalExtrafilter;
		this.exec();
	},
	
	exec: function(ignoreConfigonly) {
		this.loaded = false;
		
		ignoreConfigonly = (typeof(ignoreConfigonly) == 'undefined')?false:ignoreConfigonly;
		
		var parameters = '';
		for(i=0;i<this.queryparams.length;i++)
			parameters += ((i!=0)?'+|+':'')+this.queryparams[i];
		
		var _this = this;
		new Ajax.Request(DF.datasetQueryPath+'?dummy='+this.options.queryname+'_'+DF.Utilities.uid(), {
			method: 'post',
			parameters: {
				queryname: this.options.queryname,
				queryparams: parameters,
				extrafilter: this.extrafilter,
				configonly: (ignoreConfigonly)?false:this.options.configonly
			},
			onSuccess: function(transport) {
				if (transport.responseText.unfilterJSON().isJSON()) {
					var json = transport.responseText.evalJSON();
					
					if (!json.error) {
						if (json.success) DF.Utilities.error(json.success);

						_this.records = new Array();
						for(r=0;r<json.records.length;r++) {
							var tmp = new Array();
							for(c=0;c<json.records[r].length;c++) {
								if (json.config.length> 1)
									tmp[c] = tmp[json.config[c]["name"]] = { value:json.records[r][c], original:json.records[r][c] };
								else
									tmp[c] = { value:json.records[r][c], original:json.records[r][c] };
							}
							_this.records[_this.records.length] = tmp;
						}
		
						_this.config = json.config;
						
						_this.loaded = true;
						json = null;
						
						
						$A(_this.options.controls).each(function(item) {
							item.populate();
						});
		
						$A(_this.options.callbacks).each(function(fun) {
							fun();
						});
					} else {
						_this.config = json.config;
						_this.records = json.records;
						
						DF.Utilities.error(json.error);
					}
				} else {
					DF.Utilities.error('Dataset error - '+_this.options.queryname+' - invalid javascript JSON');	
				}
			},
			onFailure: function(transport) {
				DF.Utilities.error('Dataset error - '+_this.options.queryname+' - AJAX returned failure');
			}
		});
	}
});

/* ========================================================================== */

DF.Utilities = {
	addEvent: function(object, eventType, functionName) {
		if (object.addEventListener){
			if (eventType == 'mousewheel') 
				eventType = 'DOMMouseScroll';
			object.addEventListener(eventType, functionName, false);
			return true;
		} else if (object.attachEvent){
			var r = object.attachEvent("on"+eventType, functionName);
			return r;
		} else {
			return false;
		}
	},
	
	wheelDelta: function(e) {
		var delta = 0;
		e = (!e)?window.event:e;
	
		if (e.wheelDelta) { /* IE/Opera. */
			delta = e.wheelDelta/120;
	
			if (window.opera) /** In Opera 9, delta differs in sign as compared to IE. */
				delta = -delta;
		} else if (e.detail) { /** Mozilla case. In Mozilla, sign of delta is different than in IE. Also, delta is multiple of 3. */
			delta = -e.detail/3;
		}
	
		if (delta)
			return delta;
	
		if (e.preventDefault)
			e.preventDefault();
	
		e.returnValue = false;
		return 0;
	},
	
	resetCaret: function(ctrl, pos) {
		if (ctrl.setSelectionRange) {
			ctrl.focus();
			ctrl.setSelectionRange(pos,pos);
		} else if (ctrl.createTextRange) {
			var range = ctrl.createTextRange();
			range.collapse(true);
			range.moveEnd('character', pos);
			range.moveStart('character', pos);
			range.select();
		}
	},

	s4: function() {
		return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
	},
	
	uid: function() {
		return (this.s4()+this.s4()+"-"+this.s4()+"-"+this.s4()+"-"+this.s4()+"-"+this.s4()+this.s4()+this.s4());
	},
	
	error: function(msg) {
		if (DF.debug) {
			if (!$('dfError')) {
				var dfe = new Element('div', {
					id: 'dfError'
				});
				dfe.setStyle({
					position:'absolute',
					top:'10px',
					right:'10px',
					zIndex:'500',
					width:'100px',
					height:'600px',
					overflow:'auto',
					border:'1px solid #333333',
					background:'#ffffff',
					padding:'20px',
					color:'#cc3333',
					textAlign:'left'
				});
				
				var dfeClear = new Element('div', {
					id: 'dfErrorClear'
				});
				dfeClear.setStyle({
					position:'absolute',
					top:'11px',
					right:'11px',
					zIndex:'510',
					width:'60px',
					height:'15px',
					border:'1px solid #333333',
					borderWidth:'0px 0px 1px 1px',
					background:'#ffffff',
					color:'#333333',
					textAlign:'center',
					cursor:'pointer'
				});
				dfeClear.update('Clear');
				DF.Utilities.addEvent(dfeClear,'click',function(){ dfe.update(''); });
				
				var dfeToggle = new Element('div', {
					id: 'dfErrorToggle'
				});
				dfeToggle.setStyle({
					position:'absolute',
					top:'11px',
					right:'72px',
					zIndex:'510',
					width:'50px',
					height:'15px',
					border:'1px solid #333333',
					borderWidth:'0px 0px 1px 1px',
					background:'#ffffff',
					color:'#333333',
					textAlign:'center',
					cursor:'pointer'
				});
				dfeToggle.update('Toggle');
				DF.Utilities.addEvent(dfeToggle,'click',function(){ dfe.setStyle({ width:(dfe.getWidth() > 200)?'100px':'600px' }); });
								
				document.body.appendChild(dfe);
				document.body.appendChild(dfeClear);
				document.body.appendChild(dfeToggle);
			} else {
				var dfe = $('dfError');
			}
			if (msg.startsWith('Dataset error'))
				dfe.insert({ top:msg+'<hr>' });
			else if (msg.startsWith('Dataset warning'))
				dfe.insert({ top:'<span style="color:#ff9933;">'+msg+'</span><hr>' });
			else if (msg.startsWith('Dataset notification'))
				dfe.insert({ top:'<span style="color:#ff9933;">'+msg+'</span><hr>' });
			else if (msg.startsWith('Dataset success'))
				dfe.insert({ top:'<span style="color:#669933;">'+msg+'</span><hr>' });
			else
				dfe.insert({ top:msg+'<p>' });
		}
		
		/*
		new Ajax.Request(
			'/secure/error_report.php',
			{
				method: 'post',
				parameters: 'msg=' + URLencode(msg)
			});
		*/
	},
	
	URLencode: function(sStr) {
		return escape(sStr)
			.replace(/\+/g, '%2B')
			.replace(/\"/g,'%22')
			.replace(/\'/g, '%27')
			.replace(/\//g, '%2F');
	}
};

DF.Utilities.Shade = Class.create({
	initialize: function(options) {
		this.setOptions(options);
		
		this.id = this.options.id;
		this.message = this.options.message;
		this.status = this.options.status;
		
		this.create();
	},
	
	setOptions: function(options) {
		this.options = {
			id:			DF.Utilities.uid(),
			message:	'',
			status:		'normal' //normal,success,error
		}
		Object.extend(this.options, options || {});
	},
	
	create: function() {
		this.id = 'shade_' + this.id;
		
		var newDiv1 = new Element('div', { id: this.id });
		newDiv1.setStyle({
			width: '100%',
			height: '100%',
			position: 'fixed',
			top: '0px',
			left: '0px',
			zIndex: '100',
			background: '#000000',
			color: '#ffffff',
			display: 'block',
			opacity: .5,
			filter: 'alpha(opacity=50)'
		});				
		
		var newDiv2 = new Element('div', { id: this.id+'_msg' }).update(this.message);
		newDiv2.setStyle({
			width: '300px',
			position: 'absolute',
			top: '225px',
			left: '0px',
			zIndex: '110',
			background: '#ffffff',
			color: '#333333',
			padding: '10px 20px',
			border: '2px solid #333333',
			textAlign: 'left'
		});
		
		document.body.appendChild(newDiv1);
		document.body.appendChild(newDiv2);
		
		$(newDiv2).hide();
		//$(newDiv2).style.top = (((document.viewport.getDimensions().height / 2) - ($(newDiv2).getHeight() / 2)) + document.viewport.getScrollOffsets().top) + 'px';
		$(newDiv2).style.left = (((document.viewport.getDimensions().width / 2) - ($(newDiv2).getWidth() / 2)) + document.viewport.getScrollOffsets().left) + 'px';
		$(newDiv2).show();
	},

	update: function(txt,state,showClose) {
		if (typeof(txt) == 'undefined') txt = '';
		
		$(this.id+'_msg').update(txt);
		
		if (typeof(showClose) != 'undefined' && showClose) {
			var nControl = new Element('input', {
				type: 'button',
				value: 'Close',
				className: 'frmButton'
			}).setStyle({
				display: 'block',
				cssFloat: 'right',
				marginTop: '10px'
			});		
			
			$(this.id+'_msg').insert(nControl);
			
			var _this = this;
			DF.Utilities.addEvent(nControl, 'click', function(e) { _this.close(); });
		}

		if (typeof(state) == 'undefined' || state == 'normal')
			$(this.id+'_msg').setStyle({
				background: '#ffffff',
				color: '#333333',
				border: '2px solid #333333'
			});
		else if (state == 'success')
			$(this.id+'_msg').setStyle({
				background: '#669933',
				color: '#ffffff',
				border: '2px solid #336600'
			});		
		else if (state == 'error')
			$(this.id+'_msg').setStyle({
				background: '#CC3333',
				color: '#ffffff',
				border: '2px solid #990000'
			});
	},

	close: function() {
		if ($(this.id)) {
			document.body.removeChild($(this.id));
			document.body.removeChild($(this.id+'_msg'));
		}
	}
});