
	// -------------------------------------------------------------------
	// NewWindow
	// -------------------------------------------------------------------
	function NewWindow(theURL,winName,features) {
		if(!features) features = "width=860, height=1024,resizable=1,scrollbars=1,toolbar=1,location=1,directories=1,status=1,menubar=1,copyhistory=no";
		window.open(theURL,winName,features);
	}

	// -------------------------------------------------------------------
	// NoSubmit
	// -------------------------------------------------------------------
	function NoSubmit(charCode) {
		if(!charCode && window.event) charCode = window.event.keyCode;
		if(charCode == 13 || charCode == 3) {
			return false;
		}
		else {
			return true;
		}
	}

	// -------------------------------------------------------------------
	// DoSubmit
	// -------------------------------------------------------------------
	function DoSubmit(charCode, clickButton, form) {
		//alert("DoSubmit");
		if(!$(form)) {
			alert("No form specified for submit!");
		}
		else {
			if(!charCode) charCode = window.event.keyCode;

			if(charCode == 13 || charCode == 3) {
				button = $(form+clickButton);
				if(!button) {
					alert("The button command '"+clickButton+"' does not exist");
				}
				else {
					if(button.href) {
						location.href = button.href;
					}
					else {
						button.onclick();
					}
				}
				return false;
			}
			else {
				return true;
			}
		}
	}

	// -------------------------------------------------------------------
	// SetClass
	// -------------------------------------------------------------------
	function SetClass(itemID, newClass, formID) {
		if(formID) {
			document.forms[formID].elements[itemID].className = newClass;
		}
		else {
			var item = $(itemID);
			if(item)
				item.className = newClass;
		}
	}

	// -------------------------------------------------------------------
	// SetField
	// -------------------------------------------------------------------
	function SetField(formID, fieldID, val) {
		//alert(formID+"."+fieldID);
		if(formID) {
			if(document.forms[formID]) {
				var field = document.forms[formID].elements[fieldID];
				if(field) {
					field.value = val;
				}
				else {
					ERROR("Failed to load field "+fieldID+" from from "+formID+".");
				}
			}
		}
	}

	// -------------------------------------------------------------------
	// createCookie
	// -------------------------------------------------------------------
	function createCookie(name,value,days) {
		//alert("createCookie("+name+", "+value+", "+days+")");
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+value+expires+"; path=/";
	}

	// -------------------------------------------------------------------
	// readCookie
	// -------------------------------------------------------------------
	function readCookie(name) {
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	}

	// -------------------------------------------------------------------
	// eraseCookie
	// -------------------------------------------------------------------
	function eraseCookie(name) {
		//alert("eraseCookie("+name+")");
		createCookie(name,"",-1);
	}

//-----------------------------------------------------------------------------------------------------------------------------
//: wfx
//-----------------------------------------------------------------------------------------------------------------------------
// This provides the global namespace for all wfx code. This file should contain only definitions for
// primitive objects unique to the wfx framework.
//
// Structure:
// Any new code should be added as a new object literal within the wfx context (ie wfx.NewObject).
// Any object with an Init function will be called at domready and Unload when the user changes the
// page.
//-----------------------------------------------------------------------------------------------------------------------------
var wfx = {
	Version:"1.0",	// The version of this code
	kWeb:null,		// The base url of the site for retrieving assets 	 	(provided by PHP)
	kPage:null,		// A unique ID for the current page 				(provided by PHP)

	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Init
	//-----------------------------------------------------------------------------------------------------------------------------
	// This initializes all of the packages attached to wfx. The re param is set to true during
	// reinitialization (after an ajax load)
	//-----------------------------------------------------------------------------------------------------------------------------
	Init: function(re) {
		wfx.log("wfx.Init");

		// Run the init function for all of the wfx packages
		$each(wfx, function(p) {
			if($type(p) == "object") {
				if(p.Init) p.Init(re);
			}
		});

		if(!re) {
			//document.body.onUnload = function() {alert('test');};
			//document.body.addEvent('unload', function() {alert('test');});
			//document.body.addListener("unload", wfx.Unload);
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: ReInit
	//-----------------------------------------------------------------------------------------------------------------------------
	// This sends the Init command again but with re set to true so that each Init handler can resetup
	// if needed.
	//-----------------------------------------------------------------------------------------------------------------------------
	ReInit: function() {
		wfx.log("wfx.ReInit");
		wfx.Init(true);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Unload
	//-----------------------------------------------------------------------------------------------------------------------------
	// This is called when the page is unloaded from the browser, either by the user reloading, going to
	// a different page, or closing the browser.
	//-----------------------------------------------------------------------------------------------------------------------------
	Unload: function(re) {
		wfx.log("wfx.Unload");
		// Run the init function for all of the wfx packages
		$each(wfx, function(p) {
			if($type(p) == "object") {
				if(p.Unload) p.Unload(re);
			}
		});
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: log
	//-----------------------------------------------------------------------------------------------------------------------------
	// This is an alias for console.log but it first tests that the console is available
	//-----------------------------------------------------------------------------------------------------------------------------
	log: function(m) {
		//if(!Browser.Engine.trident) {
		//	if($defined(m) && $defined(console.log)) console.log(m);
		//}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: error
	//-----------------------------------------------------------------------------------------------------------------------------
	// This is an alias for console.log but it first tests that the console is available
	//-----------------------------------------------------------------------------------------------------------------------------
	error: function(m) {
		if(!Browser.Engine.trident) {
			if($defined(m) && $defined(console.error)) console.error(m);
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Encode
	//-----------------------------------------------------------------------------------------------------------------------------
	// All data sent through Ajax must be properly treated using url encoding with JSON structure.
	//-----------------------------------------------------------------------------------------------------------------------------
	Encode: function(d) {
		return encodeURIComponent(JSON.encode(d));
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Decode
	//-----------------------------------------------------------------------------------------------------------------------------
	// This reverses the Encode function, returning the original input string
	//-----------------------------------------------------------------------------------------------------------------------------
	Decode: function(d) {
		return decodeURIComponent(d);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Serialize
	//-----------------------------------------------------------------------------------------------------------------------------
	// This function will create a PHP serialzied object. PHP will be able to unserialize this and use it
	// directly. This is currently not used in favor of JSON.
	//-----------------------------------------------------------------------------------------------------------------------------
	Serialize: function(obj) {
		wfx.log("wfx.Utils.Init");
		var s = "";
		var total = 0;
		for(var i in obj) {
			++ total;
			s += "s-" +
				String(i).length + "-\""+String(i) + "\"--s-" +
				String(obj[i]).length + "-\"" + String(obj[i]) + "\"-";
		}
		s = "a-" + total + "-_" + s + "_";
		return (s);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Ajax - send a cmd through ajax
	//-----------------------------------------------------------------------------------------------------------------------------
	// This packages the ajax request for PHP to receive.
	// T = Target : the target div to receive the result
	// X = AJAX : signals to PHP that the request is ajax in nature
	// The T value is optional and indicates to PHP that a response
	// is expected. The R value should always be present for an ajax
	// request, otherwise PHP will return the whole page.
	//-----------------------------------------------------------------------------------------------------------------------------
	Ajax: function(params, options) {
		var 	d = {method:'post', data:"AJ=1&", evalScripts:true},
			f = $(params.form),
			t = $(params.target),
			b = null,
			p = null,
			s = null;

		wfx.log("Ajax("+params.cmd+")");
		if(options) {
			$extend(d, options);
		}

		if(params.target && !$chk(t)) {
			wfx.error("The target '"+params.target+"' does not exist");
		}
		if($chk(t)) {
		// If a target is defined, then a response is expected
			wfx.log("target:"+t.getProperty('id'));
			//b = t.getStyle('outline');
			t.setStyle('outline', '2px solid red');

			p = t.getPosition();
			s = $('ajaxStatus');
			s.setStyles({
				display:"block",
				top:p.y,
				left:p.x
			});

			d.data += "T="+params.target+"&";
			d.update = t;
			d.onComplete = function() {
				t.setStyle('outline', 'none');
				s.setStyle('display', 'none');
				// A reinit event is sent for packages to reinitialize
				window.fireEvent('reinit');
				t.fireEvent('update');
			}
		}
		wfx.log("cmd:"+params.cmd);
		d.data += "CMD="+params.cmd;
		if($chk(f)) {
			wfx.log("form:"+f.getProperty('id'));
			//f.CMD.value = params.cmd;
			d.data += "&"+f.toQueryString();
		}
		//else {
		//}
		if($chk(params.args)) d.data += "&"+params.args;

		//new Ajax('index.php', d).request();
		d.url = window.location.href;//'index.php';
		new Request(d).send();//{url:'index.php', data:d}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Response
	//-----------------------------------------------------------------------------------------------------------------------------
	// This handles a response from Ajax returning JSON plus debugging and error information. Even
	// when debugging is disabled, PHP needs a way of reporting errors through the response.
	// NOTE: This method is not called from this file but rather, Json data returned from PHP is
	// encapsulated in this function for automatic processing. So if the result of an Ajax request is Json
	// it will look like: wx.Response(jsonData, debug, error);
	//-----------------------------------------------------------------------------------------------------------------------------
	Response: function(data, dbg, err) {
		wfx.log("Response");
		if(dbg && $defined(wfx.Debug)) {
			wfx.log("show debug");
			wfx.Debug.Add(dbg);
		}
		if(err) wfx.ShowError(err);
		if(data) {
			return eval(data);
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: ShowError
	//-----------------------------------------------------------------------------------------------------------------------------
	ShowError: function(e) {
		var v = false;
		if(!$defined(wfx.ErrorWin)) {
			wfx.ErrorWin = new wfx.Window('dbg', {Title:'Debug'});
			v = wfx.ErrorWin.Views.Add('Main');
	 		wfx.ErrorWin.SetTitle("Error");
		}
		if(!v) v = wfx.ErrorWin.Views.Get('Main');
	 	v.innerHTML = e;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: IconUrl
	//-----------------------------------------------------------------------------------------------------------------------------
	IconUrl: function(i) {
		var u = wfx.kWeb+"src/core/icons/"+i;
		if(u.indexOf(".png") == -1 && u.indexOf(".gif") == -1) {
			u += ".png";
		}
		return u;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: RequestLogin
	//-----------------------------------------------------------------------------------------------------------------------------
	// Present the user with a login dialog
	//-----------------------------------------------------------------------------------------------------------------------------
	RequestLogin: function() {
		alert("RequestLogin");
	}
}; // wfx
window.addEvent('domready', wfx.Init);
window.addEvent('reinit', wfx.ReInit);
window.addListener('beforeunload', wfx.Unload);

//-----------------------------------------------------------------------------------------------------------------------------
//: Objects
//-----------------------------------------------------------------------------------------------------------------------------
// This is global storage for all objects created. Any time an object is created, a reference to it
// should be added to wfx.Objects[] so that other features (such as page unloading) can be more
// effective and iterate through all known objects.
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Objects = {
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Init
	//-----------------------------------------------------------------------------------------------------------------------------
	Init: function(re) {
		wfx.log("wfx.Objects.Init");
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Save
	//-----------------------------------------------------------------------------------------------------------------------------
	// This will iterate through all objects and have them save their current data
	//-----------------------------------------------------------------------------------------------------------------------------
	Save: function() {
		wfx.log("wfx.Objects.Save");
		for(var o in wfx.Objects) {
			wfx.log(o+":"+$type(wfx.Objects[o]));
			if($type(wfx.Objects[o]) == "element") {
				var x = wfx.Objects[o];
				x.Save();
			}
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Add
	//-----------------------------------------------------------------------------------------------------------------------------
	Add: function(obj) {
	/*
		wfx.log("wfx.Objects.Add");
		var index = obj.id;
		var x = 1;
		while($defined(wfx.Objects[index])) {
			index = obj.id+"-"+x;
			wfx.log("index:"+index);
			x++;
			if(x > 5) break;
		}
		wfx.log("index:"+index);
		wfx.Objects[index] = obj;
	*/
	}
}; // Objects

//-----------------------------------------------------------------------------------------------------------------------------
//: Object
//-----------------------------------------------------------------------------------------------------------------------------
// An object is an element extended to support data handling functions primarily for loading and
// saving to the database.
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Object = new Class({
	Implements: [Options],
	DataType:'Data',
	Data: {
	//	DataType:'Data'
	},
	options: {
		DataType:'Data',
		StoreData:true,		// This made optional so it can be turned on/off per object
		OnLoad:null		// Insert a callback method here to perform after the object is updated
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: initialize
	//-----------------------------------------------------------------------------------------------------------------------------
	initialize: function(id, el, options) {
		wfx.log(id+".initialize:"+el);

		var self = null;
		if($type(el) == "string") {
			self = $(document.createElement(el));
		}
		else {
		// Assign a prexisting element
			self = el;
		}
		$extend(self, this);
		self.show();

		self.id = id;

		self.setOptions(options);
	//	if($defined(options)) {
	//		self.setOptions(options);
	//		self.DataType = options.DataType;
	//	}
		self.SetupData();

		return self;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: SetupData
	//-----------------------------------------------------------------------------------------------------------------------------
	SetupData: function() {
//		wfx.log(this.id+".SetupData");
//		this.Data = {};
		this.id = this.id;

		if($defined(this.options)) {
			this.DataType = this.options.DataType;
		}

		wfx.Objects.Add(this);

	//	if(this.options.StoreData) {
	//		var id = (this.options.DataID) ? this.options.DataID : this.id;
	//		wfx.log("DataID:"+id);
	//		if(!$defined(wfx.Data[id])) wfx.Data[id] = {};		// Create data storage for the object
	//		this.Data = wfx.Data[id];	// Create a pointer to the wfx.Data object, so it can be accessed as this.Data
//			this.id = this.id;		// Every object must have a unique ID property
	//	}
		wfx.log("Add Object:"+this.id);
//		wfx.Objects[this.id] = this;		// Store a global reference to the object

		// Setting the DataType ensures that PHP handles the data correctly
		//if(!$defined(this.DataType)) {
//			if(this.options.DataType) this.DataType = this.options.DataType;
//			else this.DataType = "Data";
		//}
//		wfx.log("DataType:"+this.DataType);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Save
	//-----------------------------------------------------------------------------------------------------------------------------
	// This method should be overridden by any classes that have special needs to save data. During the
	// Save operation, data should only be set to the Data node and avoid making Ajax calls if possible.
	// The Data node will be saved through Ajax later in one fell swoop.
	//-----------------------------------------------------------------------------------------------------------------------------
	Save: function() {
		wfx.log(this.id+".Save");
		// The base class does nothing here.
		//wfx.Data.Save();
		//var a = this.id+"="+wfx.Data.Encode(wfx.Data[this.id]);
		//wfx.Ajax({cmd:"cmdAjaxSave", args:a}, {evalResponse:true, async:false});
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Load
	//-----------------------------------------------------------------------------------------------------------------------------
	// This provides on-demand loading of data previously stored. Unlike the Save command, this method
	// does use Ajax and handles the application of the received data through the Loaded method.
	//-----------------------------------------------------------------------------------------------------------------------------
	Load: function() {
		wfx.log(this.id+".Load");
		wfx.Ajax({cmd:"cmdAjaxLoad", args:"ID="+this.id+"&DataType="+this.DataType},
			{evalResponse:false, onComplete:this.Loaded.bind(this)});
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Loaded
	//-----------------------------------------------------------------------------------------------------------------------------
	// This receives the result of an Ajax request made by the Load method. The response is expected
	// to be JSON or executable javascript code that will evaluate to an object or value. The data is
	// updated to the Data node, only affecting new values. A class may override this method to
	// perform any custom updating, or use it like notification.
	//-----------------------------------------------------------------------------------------------------------------------------
	Loaded: function(response, skipEvent) {
		wfx.log(this.id+".Loaded");

		if(!$defined(response)) {
			wfx.error("Invalid response received in wfx.Object.Loaded");
			return;
		}
		if(response.contains("Fatal error")) {
			wfx.ShowError(response);
			return;
		}
		var html = undefined;
		if(response.contains("---JSON---")) {
			var data = eval('('+response+')');
			if($defined(data)) {
				//if(!$defined(wfx.Data[this.id])) wfx.Data[this.id] = {};
				//$extend(wfx.Data[this.id], data);
				$extend(this.Data, data);
			}
		}
		else {
			wfx.log("RESPONSE IS HTML");
			html = response;
		}
		if(!skipEvent) this.fireEvent('update');

		if(this.options.OnLoad) this.options.OnLoad(this, html);
	}
}); // Object
//wfx.Object.implement(new Options);

//-----------------------------------------------------------------------------------------------------------------------------
//: Page
//-----------------------------------------------------------------------------------------------------------------------------
// This manages the data for the current page. A page is a fixed url location and does not contain
// any content itself but wraps a document in the appropriate headers and manages the general
// access settings. The content of the page comes from loading the assigned document or procedure.
// The page headers are generated by PHP, whereas this object is here just to provide support.
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Page = new Class({
	Extends: wfx.Object,
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: initialize
	//-----------------------------------------------------------------------------------------------------------------------------
	// Since only one page can be loaded at a time, the constructor only returns a reference to the
	// already existing page object. This does not call the Object initialize method.
	//-----------------------------------------------------------------------------------------------------------------------------
	initialize: function() {
		wfx.log("wfx.Page.initialize:"+wfx.kPage);

		var p = $(wfx.kPage);
		if(p) {
			$extend(p, this);

			p.id = wfx.kPage;
			p.DataType = "Page";
		}
		//p.SetupData();

		return p;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Load
	//-----------------------------------------------------------------------------------------------------------------------------
	Load: function() {
		wfx.log(this.id+".Load");
		//wfx.Ajax({cmd:"cmdAjaxLoad", args:"ID="+this.id+"&DataType=Page"},
		//	{evalResponse:false, onComplete:this.Loaded.bind(this)});
		this.parent();
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Save
	//-----------------------------------------------------------------------------------------------------------------------------
	Save: function() {
		wfx.log(this.id+".Save");
		//this.Data.Content = wfx.Data.Encode(this.innerHTML);
		this.parent();
	}
}); // Page

//-----------------------------------------------------------------------------------------------------------------------------
//: View
//-----------------------------------------------------------------------------------------------------------------------------
// This creates a special div that manages the visibility of its children. Each child is added with a
// unique id and can be switched on or off by the View. Use Add for inserting new content and then
// use Select for choosing which child is visible. Note: for a visible user interface use Tabs.
//-----------------------------------------------------------------------------------------------------------------------------
wfx.View = new Class({
	Extends: wfx.Object,
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: initialize
	//-----------------------------------------------------------------------------------------------------------------------------
	initialize: function(id, options) {
		wfx.log("wfx.View.Init");
		var self = this.parent(id, 'div', options);
		self.set({'class':'view', id:id});
		return self;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Add
	//-----------------------------------------------------------------------------------------------------------------------------
	// This adds a new layer to the View. It is highly recommended that you use an ID that will be
	// universally unique, though the Select and Get functions will only search within the View.
	//-----------------------------------------------------------------------------------------------------------------------------
	Add: function(id, content) {
		wfx.log("wfx.View.Add("+id+")");

		var v = this.addElement('div', {id:id});
		if($defined(content)) {
			if($type(content) == "string") {
				v.innerHTML = content;
			}
			else {
				v.appendChild(content);
			}
		}

		this[id] = v;
		this.Select(id);

		v.setStyles({
			'width':'100%',
			'height':'100%'
		});
		v.onResize = function() {
			wfx.log("subView.onResize");
			v.getChildren().each( function(child) {
				if(child.onResize) {
					child.onResize();
				}
			});
		};
		return v;

	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Select
	//-----------------------------------------------------------------------------------------------------------------------------
	// Show the specified view and hide all others.
	//-----------------------------------------------------------------------------------------------------------------------------
	Select: function(view) {
		wfx.log("wfx.View.Select:"+view);
		this.getChildren().each(
			function(ch) {
				if(ch.id == view) {
					wfx.log(ch.id+".show");
					ch.show();
				}
				else {
					wfx.log(ch.id+".hide");
					ch.hide();
				}
			}
		);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Get
	//-----------------------------------------------------------------------------------------------------------------------------
	// Return the specified view element. This is more reliable than using $(id) incase the id is not unique.
	//-----------------------------------------------------------------------------------------------------------------------------
	Get: function(view) {
		wfx.log("wfx.View.Get:"+view);
		var ch = this.getChildren();
		var found = false;
		for(var x=0; x<ch.length; x++) {
			if(ch[x].id == view) {
				found = ch[x];
				break;
			}
		}
		return found;
	}
});

//-----------------------------------------------------------------------------------------------------------------------------
//: Window
//-----------------------------------------------------------------------------------------------------------------------------
// This Window class creates virtual window using DHTML. This is not to be confused with a
// traditional window, which is a Popup in wfx. Windows automatically store their position and size.
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Window = new Class({
	Extends: wfx.Object,
	options: {
		Type:'',
		Title:'',
		Width:400, //'250px',
		Height:300, //'250px',
		Movable:true,
		Resizable:true,
		Closable:true,
		Load:true,
		Visible:true,
		Bounded:false,
		onClose:$empty
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: initialize
	//-----------------------------------------------------------------------------------------------------------------------------
	initialize: function(id, options) {
		wfx.log(id+".initialize");
		options.DataType = "Data";
		var self = this.parent(id, 'div', options);
		self.Create();
		document.body.appendChild(self);//$('app')

		return self;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Create
	//-----------------------------------------------------------------------------------------------------------------------------
	Create: function() {
		wfx.log(this.id+".Create");
		var c = this.options.Type ? "vw"+this.options.Type : "";
		this.setProperty('class', 'vw '+c);

		//for(var o in this.options) {
		//	wfx.log(this.id+".option."+o+"="+this.options[o]);
		//}

		this.Header  		= this.addElement('div', {id:this.id+'H', 'class':'vwH'});
		this.Title 			= this.Header.addElement('div', {id:this.id+'T', 'class':'vwT'});
		this.CloseButton 	= this.Header.addElement('div', {id:this.id+'X', 'class':'vwX'});
		this.Content  		= this.addElement('div', {id:this.id+'C', 'class':'vwC'});
		this.Footer  		= this.addElement('div', {id:this.id+'F', 'class':'vwF'});
		this.FooterText  	= this.Footer.addElement('div', {id:this.id+'FT', 'class':'vwFT'});
		this.ResizeHandle 	= this.Footer.addElement('div', {id:this.id+'R', 'class':'vwR'});

		if(this.options.Title) {
			this.Title.innerHTML = this.options.Title;
		}
		var cont = '';
		if(this.options.Bounded) cont = document.body;//'body';
		if(this.options.Movable) {
			this.makeDraggable({
				handle:this.Header,
				onStart: this.StartDrag.bind(this),
				onComplete: this.EndDrag.bind(this),
				container:cont
			});
		}

		if(this.options.Resizable) {
			this.makeResizable({
				handle:this.ResizeHandle,
				onDrag: this.onResize.bind(this),
				onComplete: this.Save.bind(this)
			});
		}
		if(this.options.Closable) {
		// Add a close button
			this.CloseButton.addEvent('mousedown', function(ev) { ev.stopPropagation(); });
			this.CloseButton.addEvent('mouseup', this.Close.bindWithEvent(this));
		}
		else {
			this.CloseButton.hide();
		}
		//if(this.options.onClose) {
		//	this.addEvent('close', this.options.onClose);
		//	this.options.onClose();
		//}
		if(this.options.Load) {
			this.Load();
			//if(this.Data.V) this.show();
		}

		// Setup the internal view for managing the window content
		this.Views = new wfx.View(this.id+'View');
		this.Content.appendChild(this.Views);

		this.SetSize(this.options.Width, this.options.Height);
		//this.Load();
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: SetTitle
	//-----------------------------------------------------------------------------------------------------------------------------
	SetTitle: function(t) {
		wfx.log(this.id+".SetTitle");
		this.Title.innerHTML = t;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: StartDrag
	//-----------------------------------------------------------------------------------------------------------------------------
	StartDrag: function() {
		wfx.log(this.id+".StartDrag");
		this.setOpacity(0.5);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: EndDrag
	//-----------------------------------------------------------------------------------------------------------------------------
	EndDrag: function() {
		wfx.log(this.id+".EndDrag");
		this.setOpacity(1);
		this.Save();
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Check
	//-----------------------------------------------------------------------------------------------------------------------------
	Check: function() {
		wfx.log(this.id+".Check");
		var w = window.getSize().size;
		var c = this.getCoordinates();

		this.setStyles({
			left:(c.left <= 0) ? 0 : (c.left < w.x) ? c.left : w.x - c.width,
			top:(c.top <= 0) ? 0 : (c.top < w.y) ? c.top : w.y - c.height
		});
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: SetSize
	//-----------------------------------------------------------------------------------------------------------------------------
	SetSize: function(w, h) {
		wfx.log(this.id+".SetSize("+w+","+h+")");
		this.setStyles({
			width:w+"px",
			height:h+"px"
		});
		this.Data.W 	= w;
		this.Data.H 	= h;
		this.onResize();
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Save
	//-----------------------------------------------------------------------------------------------------------------------------
	Save: function() {
		wfx.log(this.id+".Save");
		if(this.isVisible()) {
			var p = this.getPosition();
			var s = this.getInnerSize().size;
			this.Data.X 	= p.x;
			this.Data.Y 	= p.y;
			this.Data.W 	= s.x;
			this.Data.H 	= s.y;
			this.Data.V	= this.isVisible() ? 1 : 0;
			wfx.log("visible:"+this.Data.V);
		}
		this.parent();
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Check
	//-----------------------------------------------------------------------------------------------------------------------------
	Check: function() {
		wfx.log(this.id+".Check");

		var w = window.getSize();//.size;

		var d = this.Data;
		this.Data.X 	= (isNaN(d.X) || d.X <= 0) ? 0 : (d.X < w.x) ? d.X : w.x - d.W;
		this.Data.Y 	= (isNaN(d.Y) || d.Y <= 0) ? 0 : (d.Y < w.y) ? d.Y : w.y - d.H;
		this.Data.W 	= (isNaN(d.W) || d.W <= 0) ? 150 : (d.W < w.x) ? d.W : w.x;
		this.Data.H 	= (isNaN(d.H) || d.H <= 0) ? 150 : (d.H < w.y) ? d.H : w.y;

		wfx.log(this.Data);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Loaded
	//-----------------------------------------------------------------------------------------------------------------------------
	Loaded: function(response) {
		this.parent(response);
		wfx.log(this.id+".Loaded (Window)");
		this.Check();
		wfx.log(this.Data);
		this.setStyles({
			left:this.Data.X,
			top:this.Data.Y,
			width:this.Data.W,
			height:this.Data.H
		});

		if(this.Data.V || (!$defined(this.Data.V) && this.options.Visible)) {
			this.Show();
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Close
	//-----------------------------------------------------------------------------------------------------------------------------
	Close: function(ev) {
		wfx.log(this.id+".Close");
		this.fireEvent('onClose');
		this.Hide();
		if(ev) ev.stopPropagation();
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Show
	//-----------------------------------------------------------------------------------------------------------------------------
	Show: function() {
		wfx.log(this.id+".Show");
		this.show();
		this.Save();
		this.onResize();
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Hide
	//-----------------------------------------------------------------------------------------------------------------------------
	Hide: function() {
		wfx.log(this.id+".Hide");
		this.Save();
		this.Data.V = 0;
		this.hide();
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Toggle
	//-----------------------------------------------------------------------------------------------------------------------------
	Toggle: function() {
		wfx.log(this.id+".toggle");
		var v = this.isVisible() ? 1 : 0;
		if(v) {
			this.Save();
			this.Data.V = 0;
		}
		this.toggle();
		if(!v) this.Save();
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: onResize
	//-----------------------------------------------------------------------------------------------------------------------------
	onResize: function() {
		wfx.log(this.id+".onResize");
		var 	s 	= this.getSize(),
			hs	= this.Header.getSize(),
			fs	= this.Footer.getSize();

		wfx.log(s.y);
		var height = s.y - (hs.y + fs.y);
		this.Content.setStyle("height", height);
		this.Views.setStyle("height", height);

		// Resize each child view to match
		this.Views.getChildren().setStyle("height", height);
		this.Views.getChildren().each(function(child) {child.onResize();});

		// broadcast the event in case any other actions are needed
		this.fireEvent('resize');
	}
}); // Window


//-----------------------------------------------------------------------------------------------------------------------------
//: Button
//-----------------------------------------------------------------------------------------------------------------------------
// This creates a button object which can be used with or without forms. This creates simple text,
// icon or image based buttons.
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Button = new Class({
	Extends: wfx.Object,
	Disabled:false,
	EventsBackup:null,
	OutlineBackup:null,
	options: {
		Action:null,
		Style:'button',
		Label:'New Button',
		Link:'javascript:;',
		Submit:false,
		Icon:false
		// TODO: Implement Image
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: initialize
	//-----------------------------------------------------------------------------------------------------------------------------
	initialize: function(id, options, props) {
		wfx.log("wfx.Button: "+id);
		var self = this.parent(id, 'a', options);

		self.setProperty('class', self.options.Style);
		self.set(props);
		self.innerHTML = self.options.Label;

		if(self.options.Icon) {
			var img = self.addElement('img');
			img.setProperty('src', wfx.kWeb+"src/core/icons/"+self.options.Icon+".png");
		}
		if(self.options.Link) {
			self.setProperty('href', self.options.Link);
		}
		if(self.options.Action) {
			wfx.log("buttonClick");
			self.addEvent('click', self.options.Action);
		}

		self.EventsBackup = $(document.createElement("a"));
		//	self.addEvent('mouseover', function() {
		//		var pos = getPosition(this);
		//		wfx.log("x:"+pos.x+", y:"+pos.y);
		//	});

		//if(props.disabled == true) self.Disable(false);
		//else self.Disable(false);

		return self;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Enable
	//-----------------------------------------------------------------------------------------------------------------------------
	Enable: function() {
		wfx.log("wfx.Button.Enable");
		if(this.Disabled) {
		// We make sure the control has already been disabled once, and then restore the saved
		// backup of its events.
			this.setProperty('href', this.options.Action);
			this.cloneEvents(this.EventsBackup);
			this.setStyle('outline', this.OutlineBackup);
			this.setProperty('class', this.options.Style);
			this.Disabled = false;
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Disable
	//-----------------------------------------------------------------------------------------------------------------------------
	Disable: function() {
		wfx.log("wfx.Button.Disable");
		// We need to disable the button. To do this we first need to make a backup of its events
		// and then clear them so nothing happens when the user clicks on it.
		if(!this.Disabled) {
			this.EventsBackup.cloneEvents(this);
			this.setProperty('href', null);
			this.removeEvents();
			this.OutlineBackup = this.getStyle('outline');
			this.setProperty('class', 'button_disabled');
			this.Disabled = true;
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: SetLabel
	//-----------------------------------------------------------------------------------------------------------------------------
	SetLabel: function(label) {
		wfx.log("wfx.Button.SetLabel");
		this.innerHTML = label;
	}

}); // Button

//-----------------------------------------------------------------------------------------------------------------------------
//: Input
//-----------------------------------------------------------------------------------------------------------------------------
// This creates various types of form inputs. Notice that this class is not derrived from Object
// because it doesn't make sense for every input to individually store its value. Instead, you must
// use a form. Don't overlook all the properties you can set to define the type of control you need.
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Input = new Class({
	Extends: wfx.Object,
	options: {
		Label:false
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: initialize
	//-----------------------------------------------------------------------------------------------------------------------------
	initialize: function(id, options, props) {
		wfx.log("wfx.Input");
		var type = "input";
		if($defined(options.Type)) {
			if(options.Type == "textarea") {
				type = "textarea";
			}
			else {
				props.type = options.Type;
			}
		}

		var self = this.parent(id, type, options);
		self.set(props);

		// Both the id and name properties must be set for form elements to submit
		self.setProperty('name', id);

		if(type == "textarea") {
			self.innerHTML = options.Value;
		}
		else {
			self.setProperty('value', options.Value);
		}

		//if(self.options.Data) {
		//	self.Data = self.options.Data;
		//}
		self.addEvent('change', self.onChange.bind(self));
		return self;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: onChange
	//-----------------------------------------------------------------------------------------------------------------------------
	onChange: function() {
		wfx.log(this.id+".onChange");
		if(this.Data) {
			wfx.log('Data:'+this.value);
			this.Data = this.value;
		}
	}
}); // Input

//-----------------------------------------------------------------------------------------------------------------------------
//: $form
//-----------------------------------------------------------------------------------------------------------------------------
// This is a quick wrapper for form elements already on the page. You can get the form object by
// calling $form(formID) which will return the element extended with the Form.prototype.
//-----------------------------------------------------------------------------------------------------------------------------
function $form(el) {
	var f = $(el);
	if(!$chk(f)) {
		wfx.log('no form');
		return false;
	}
	var found = false;
	var id = f.getProperty('id');
	wfx.log("$form("+id+")");
	if($chk(f)) {
		while($type(f) == "element") {
			if(f.get('tag') == "form") {
				found = true;
				break;
			}
			else {
				f = f.getParent();
				wfx.log("getParent");
				if(!$chk(f)) {
					wfx.error("Failed to find form element for "+id);
					break;
				}
			}
		}
		wfx.log("$form: id="+id+", form="+f.getProperty('id'));
	}
	else wfx.error("The form '"+id+"' does not exist");
	if(!found) f = false;
	else $extend(f, wfx.Form.prototype);
	return f;
}

//-----------------------------------------------------------------------------------------------------------------------------
//: Form
//-----------------------------------------------------------------------------------------------------------------------------
// This creates and manages an HTML form.
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Form = new Class({
	Implements: [Options],
	//Extends: wfx.Object,
	Table:null,
	Fields:{},
	//Implements: [Options, Events],
	options: {
		Layout:'Table',  //Div
		UseAjax:false,
		UseJson:false,
		AutoCreate:false,
		onChange:false,
		Validate:null
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: initialize
	//-----------------------------------------------------------------------------------------------------------------------------
	initialize: function(id, options, props) {
		wfx.log("wfx.Form:"+id);
		var self = $(document.createElement('form'));//this.parent(id, 'div', options);
		$extend(self, this);
		self.show();
		self.id = id;
		self.setOptions(options);

		if(!$defined(props)) props = {};
		if(!$defined(props.method)) props.method = "POST";
		self.set(props);

		if(self.options.Layout == 'Table') {
			self.Table = self.addElement('table');
			self.Table.setProperties({'class':'FormTable', 'border':0});
		}
		if(self.options.AutoCreate) {
			self.AutoCreate();
		}
		self.addEvent("change", self.onChange.bind(self));
		//self.Data.Modified = false;

	//	var parentWin = this.GetParentWindow();
	//	parentWin.addEvent('resize', function() {
	//		wfx.log('textarea resize');
	//		i.AutoHeight();
	//	});

		return self;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: AutoHeight
	//-----------------------------------------------------------------------------------------------------------------------------
	AutoHeight: function() {
		wfx.log('wfx.Form.AutoHeight');
	//	var size = this.getParent().getSize();
	//	this.setStyle('height', size.y);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: AutoCreate
	//-----------------------------------------------------------------------------------------------------------------------------
	AutoCreate: function(exclude) {
		for(var d in this.Data) {
			this.AddInput(d, {type:'text'}, {Label:d});
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: AddInput
	//-----------------------------------------------------------------------------------------------------------------------------
	AddInput: function(id, options, props) {
		wfx.log("AddInput: "+id);

		if(this.Fields[id]) {
			wfx.error("Input["+id+"] already exists");
			return null;
		}
		if(!$defined(props)) props = {};
		//if($defined(this.Data[id])) {
			wfx.log("setData:"+id);
			//if(!$defined(options.Value)) options.Value = this.Data[id];
			//options.Data = this.Data[id];
		//}
		var i = new wfx.Input(id, options, props);

		if(i.options.Type == 'textarea') {

		//	if(i.options.AutoHeight) {
		//		i.AutoHeight = function() {
		//			wfx.log('textarea AutoHeight');
		//			var s = i.getParent().getSize();
		//			i.setStyle('height', s.y-10);
		//		};
		//		var parentWin = this.GetParentWindow();
		//		parentWin.addEvent('resize', function() {
		//			wfx.log('textarea resize');
		//			i.AutoHeight();
		//		});
		//	}

			this.AddRow(i.options.Label, null, {'height':'20px'});
			this.AddRow(null, i);
		}
		else {
			this.AddRow(i.options.Label, i, {'height':'20px'});
		}

		this.Fields[id] = i;

		var self = this;
		if(options.Submit) {
			i.addEvent('keypress', function(ev) {
				if(ev.key == 'enter') {
					ev.preventDefault();
					self.Submit(options.Submit);
				}
			});
		}

		//return i;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: AddButton
	//-----------------------------------------------------------------------------------------------------------------------------
	AddButton: function(id, options, props) {
		wfx.log("AddButton: "+id);
		if(this.Fields[id]) {
			wfx.error("Button["+id+"] already exists");
			return null;
		}
		if(!$defined(props)) props = {};
		//if(!$defined(options.Value) && $defined(this.Data[id])) {
		//	options.Value = this.Data[id];
		//	options.Data = this.Data;
		//}
		var b = new wfx.Button(id, options, props);
		//if(b.options.NewRow) this.AddRow('', b);
		//else
		this.appendChild(b);
		var self = this;
		if(options.Submit) {
			b.addEvent('click', function() { self.Submit(id); });
		}
		this.Fields[id] = b;
		return b;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: AddRow
	//-----------------------------------------------------------------------------------------------------------------------------
	AddRow: function(col1, col2, props) {//label, field, id) {
		wfx.log("AddRow");

		var tr = 'tr';
		var td = 'td';
		if(this.options.Layout != 'Table') {
			tr = 'div';
			td = 'div'
		}

		// Create row container
		var r = new Element(tr, {'class':'row'});
		if($defined(props)) r.setProperties(props);

		var col1span = 1;
		var col1class = "label";
		var col2span = 1;
		var col2class = "field";

		if(col1 && !col2) {
			col1span = 2;
			col1class = "labelspan";
		}
		if(!col1 && col2) {
			col2span = 2;
			col2class = "fieldspan";
		}

		if(col1) {
		// Create a 2 column row with label and field
			var l = r.addElement(td, {'class':col1class, 'colspan':col1span});
			l.innerHTML = col1;
		}
		if(col2) {
			var f = r.addElement(td, {'class':col2class, 'colspan':col2span});
			if($type(col2) == "string") {
				f.innerHTML = col2;
			}
			else {
				f.appendChild(col2);
			}
		}

		if(this.options.Layout == 'Table') {
			wfx.log('Table Layout');
			this.Table.appendChild(r);
		}
		else {
			this.appendChild(r);
		}

		//r.addElement('div', {'class':'rowMin'});
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: AddRowHeadings
	//-----------------------------------------------------------------------------------------------------------------------------
	AddRowHeadings: function(label, field) {
		this.AddRow(label, field, 'heading');
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: SetValue
	//-----------------------------------------------------------------------------------------------------------------------------
	SetValue: function(name, v) {
		wfx.log("SetValue:"+name+"="+v);
		if(this.Fields[name]) {
			if($defined(this.Fields[name].value))
				this.Fields[name].value = v;
			//else
			//	this.Fields[name].innerHTML = v;
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: GetValue
	//-----------------------------------------------------------------------------------------------------------------------------
	GetValue: function(name) {
		wfx.log("GetValue:"+name);
		if(this.Fields[name]) {
			return this.Fields[name].value;
		}
		return null;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: GetData
	//-----------------------------------------------------------------------------------------------------------------------------
	GetData: function() {
		wfx.log("GetData()");
		var data = { DataType:this.DataType };//new Array();
		for(var i in this.Fields) {
		//	wfx.log(i);
			if($defined(this.Fields[i].value)) {
				data[i] = this.Fields[i].value;
				wfx.log(i+"="+this.Fields[i].value);
			}
		}
		return data;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: GetParentWindow
	//-----------------------------------------------------------------------------------------------------------------------------
	GetParentWindow: function() {
		wfx.log("wfx.App.GetParentWindow");
		var parent = this.getParent('.vw');
		if(!parent) parent = document;
		return parent;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: ReloadValues
	//-----------------------------------------------------------------------------------------------------------------------------
	ReloadValues: function() {
		wfx.log("ReloadValues");
		if(this.options.Data) {
			for(var i in this.Fields) {
		//		wfx.log(i);
				if($defined(this.Fields[i].value)) {
					this.Fields[i].value = this.options.Data[i];
					wfx.log(i+"="+this.Fields[i].value);
				}
			}
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Submit
	//-----------------------------------------------------------------------------------------------------------------------------
	Submit: function(withCmd) {
		wfx.log("wfx.Form.Submit("+withCmd+")");
		if(this.options.Validate) {
			var v = this.options.Validate(this, withCmd);
			if(!v) {
				wfx.log("INVALID");
				return;
			}
			else {
				wfx.log("VALID");
			}
		}
		if(!this.options.UseAjax) {
		// When Ajax is not being used, the traditional approach of submitting the form and refreshing
		// the page is used. We do this by calling the built-in JavaScript form submit method.
			this.submit();
		}
		else {
		// When Ajax is enabled for the form, the submit is passed in the background without reloading
		// the page. There are two ways in which the form data can be handled, shown below.
			if(this.options.UseJson) {
			// Here we take the form as an object (or the object it represents) and encode it with
			// Json as a single argument.
				wfx.log("JSON");
				var arg = this.GetValue('ID')+"="+wfx.Encode(this.GetData());
			//	wfx.log("arg:"+arg);
				wfx.Ajax({cmd:withCmd, args:arg}, {evalResponse:true});
			}
			else {
			// Using a more traditional approach, each form value will be added to the query string as
			// a variable. This is done automatically by wfx.Ajax
				wfx.Ajax({cmd:withCmd, form:this.id}, {evalResponse:true});
			}
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: onChange
	//-----------------------------------------------------------------------------------------------------------------------------
	onChange: function(event) {
		wfx.log("wfx.Form.onChange");
		//this.Data.Modified = true;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: onResize
	//-----------------------------------------------------------------------------------------------------------------------------
	onResize: function() {
		wfx.log("wfx.Form.onResize");
		wfx.log(this.id);
		//this.Data.Modified = true;
		var size = this.getParent().getSize();
		this.setStyle('height', size.y);

		this.getElements('textarea').each( function(input) {
			//if(input.type == "textarea") {
				var s = input.getParent().getSize();
			//	input.setStyle('height', s.y-20);
				wfx.log("input height:"+s.y);
			//}
		});
	}
});
//wfx.Form.implement(new Options, new Events);

//-----------------------------------------------------------------------------------------------------------------------------
//: Tabs
//-----------------------------------------------------------------------------------------------------------------------------
// This creates a multi-tabbed view, showing and hiding the content based on user interation.
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Tabs = new Class({
	Extends: wfx.Object,
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: initialize
	//-----------------------------------------------------------------------------------------------------------------------------
	initialize: function(id, options) {
		wfx.log("wfx.Tabs.Init");
		var self = this.parent(id, 'div', options);
		self.set({'class':'tabs'});

		// create a UL for the self
		self.headings = self.addElement('ul', {'class':'tabs', id:id+"H"});

		// create a UL for the tab content
		self.contents = self.addElement('ul', {'class':'tabsC', id:id+"C"});

		return self;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Add
	//-----------------------------------------------------------------------------------------------------------------------------
	Add: function(id, name, content) {
		wfx.log("wfx.Tabs.Add");

		var h = this.headings.addElement('li', {id:id});
		if($type(name) == "string") {
			h.innerHTML = name;
		}
		else {
			h.appendChild(name);
		}
		var c = this.contents.addElement('li', {id:id+"C"});
		if($type(content) == "string") {
			c.innerHTML = content;
		}
		else {
			c.appendChild(content);
		}

		h.addEvents({
			click:this.Select.bind(this, h)
		});
		h.setStyle('cursor', 'pointer');

		//this.Select(h);
		return h;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Select
	//-----------------------------------------------------------------------------------------------------------------------------
	Select: function(tabID) {
		wfx.log("wfx.Tabs.Select");

		wfx.log("select: "+tabID);

		var hs = this.headings.getElements('li');
		var tab = null;

		if($type(tabID) == 'string') {
		// We need to find the tab object by name or id
			for(var i=0; i<hs.length; i++) {
				wfx.log("li: "+hs[i].id);
				if(hs[i].id == tabID || hs[i].innerHTML == tabID) {
					tab = hs[i];
					break;
				}
			}
		}

		if(!tab) {
			wfx.error("The specified tab ("+tabID+") does not exist");
			return;
		}

		// loop through all the tabs and deselect all but the one
		hs.each(
			function(li) {
				wfx.log("li: "+li.getProperty('id'));
				li.setProperty('class', (li == tab) ? 'selected' : 'deselect');
			}
		);

		// loop through all the content and hide all but the selected
		var c = $(tab.getProperty('id')+"C");
		this.contents.getElements('li').each(
			function(li) {
				wfx.log("li: "+li.getProperty('id'));
				li.setStyle('display', (li == c) ? 'block' : 'none');
			}
		);
	}
}); // Tabs


//-----------------------------------------------------------------------------------------------------------------------------
//: Menu
//-----------------------------------------------------------------------------------------------------------------------------
// This creates an application-style menu bar with hierarchical submenus. This class uses the
// modalizer to help control interactivity, since mouse over and out events are unreliable on their
// own. The display of the menu is controlled by a simple set of CSS rules based on wfxMenu.
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Menu = new Class({
	Extends: wfx.Object,
	Implements: [Events, Modalizer],
	options: {
		Overlap:10
	},
	modalOptions: {},
	Menus: null,
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: initialize
	//-----------------------------------------------------------------------------------------------------------------------------
	initialize: function(id, options) {
		wfx.log("wfx.Menu.Init");

		this.setModalStyle({zIndex:1000, 'background-color':'none', opacity:0.5});
		var self = this.parent(id, 'div', options);
		self.modalOptions.onModalHide = this.Dismiss.bind(self);
		self.set({'class':'wfxMenu', id:id});

		// Create the primary list element for the menu. This will be the horizontal bar.
		self.Menus = [self.addElement('ul', {id:id+"UL"})];

		return self;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Invoke
	//-----------------------------------------------------------------------------------------------------------------------------
	// This method instantiates the first submenu, using the modalizer to temporarily capture mouse
	// input from the whole page. The floating submenu is then displayed in front.
	//-----------------------------------------------------------------------------------------------------------------------------
	Invoke: function(parentItem) {
		wfx.log("wfx.Menu.Invoke");
		this.modalShow();

		// When the modalizer is invoked, it pops over the original menu. To prevent the modalizer
		// from immediately disappearing (due to mouseover) we need to create a copy of the menu
		// item that will float above the modalizer layer temporarily.
		wfx.log("cloning: "+parentItem.id);
		var pos = parentItem.getPosition();
		this.menuTemp = parentItem.clone(true, true);
		parentItem.parentNode.appendChild(this.menuTemp);
		this.menuTemp.setStyles({'z-index':1001, position:'absolute', top:pos.y, left:pos.x});//, background:'red'
		this.menuTemp.addClass('over');

		var m = this.toElement();
		var self = this;
		m.addEvent('mouseover', function(event) {
			self.modalHide();
			event.stop();
		});
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Dismiss
	//-----------------------------------------------------------------------------------------------------------------------------
	Dismiss: function() {
		wfx.log("wfx.Menu.Dismiss");
		this.menuTemp.dispose();
		for(var n=1; n<this.Menus.length; n++) {
			this.Menus[n].invisible();
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Add
	//-----------------------------------------------------------------------------------------------------------------------------
	// This adds a series of list items (LI) to an existing menu. If the list contains sub-children, this
	// will be expanded into a submenu. Each submenu is a virtual child of a menu item.
	//-----------------------------------------------------------------------------------------------------------------------------
	Add: function(items, parentItem, depth) {
		wfx.log("wfx.Menu.Add");

		if(!depth) depth = 0;
		var parentMenu = null;
		if(!$defined(parentItem)) {
			parentItem = null;
			parentMenu = this.Menus[0];
		}
		else {
			parentItem = $(parentItem);
			parentMenu = $(parentItem.id+"UL");
		}

		if(!$defined(this.Items)) this.Items = [];

		var self = this;
		for(var i in items) {
			wfx.log("   Adding:"+i);
			this.Items[i] = parentMenu.addElement('li', {id:i});
			//this.Items[i] = newItem;

			if(i == "divider") {
				this.Items[i].innerHTML = "<hr>";
			}
			else {
				var h = "";
				h += "<div class='wfxMenuIcon'>";
				if(items[i].Icon) {
					h += "<img src='"+wfx.IconUrl(items[i].Icon)+"'>";
				}
				h += "</div>";
				h += "<div class='wfxMenuLabel'>"+items[i].Label+"</div>";
				h += "<div class='wfxMenuStatusIcon'>";
				if($defined(items[i].Children)) {
					h += "<img src='"+wfx.IconUrl('resultset_next')+"'>";
				}
				else
				if(items[i].StatusIcon) {
					h += "<img id='"+i+"Status' src='"+wfx.IconUrl(items[i].StatusIcon)+"'>";
				}
				h += "</div>";
				this.Items[i].innerHTML = h;

				if($defined(items[i].Status)) this.Items[i].Status = items[i].Status;


				this.Items[i].addEvent('mouseover', function(event) {
					this.addClass('over');

					// We need to additionally close all submenus. If the current menu item has a
					// submenu, it will be displayed by the other mouseover event installed.
					for(n=1; n<self.Menus.length; n++) {
						if(depth <= self.Menus[n].depth) {
							self.Menus[n].invisible();
						}
					}
				});

				this.Items[i].addEvent('mouseout', function(event) {
					this.removeClass('over');
					event.stop();
				});
				this.Items[i].Action = items[i].Action;
				this.Items[i].addEvent('click', function(event) {
					this.Action.call();
					self.Dismiss();
					event.stop();
				});
				for(var e in this.Items[i].$events) {
					wfx.log("event:"+e);
				}

				if($defined(items[i].Children)) {
					this.AddList(this.Items[i], parentMenu, depth);
					this.Add(items[i].Children, this.Items[i], depth+1);
				}
			}
		}
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: AddList
	//-----------------------------------------------------------------------------------------------------------------------------
	// This adds a new submenu for an item. To avoid problems with Z order and nested divs, we'll
	// create the new UL elements at the document level.
	//-----------------------------------------------------------------------------------------------------------------------------
	AddList: function(parentItem, parentMenu, depth) {
		parentItem = $(parentItem);
		wfx.log("wfx.Menu.AddList:"+parentItem.id);

		// Create a new menu layer. It is placed at the end of the document to avoid display issues
		// however this also means that the UL items do not have a parent-child relationship in the DOM.
		// Relationships are stored instead by this class and the display mimics hierarchy using dynamic
		// positions.
		var n = this.Menus.length;
		wfx.log("menu count:"+n);
		this.Menus[n] = document.createElement('ul');
		this.Menus[n].set({'class':'wfxMenuSub'});
		this.Menus[n].id = parentItem.id+"UL";
		this.Menus[n].invisible();
		this.Menus[n].depth = depth;
		this.Menus[n].parentMenu = parentMenu;
		document.body.appendChild(this.Menus[n]);

		var self = this;
		var menu = this.Menus[n];
		parentItem.addEvent('mouseover', function() {
			wfx.log("mouseover "+this.id);
			if(depth == 0) {
				self.Invoke(parentItem);
			}
			var coords = this.getCoordinates();
			var pos;

			if(!depth) {
			// Align the list as a drop down menu
				pos = {x: coords.left, y: coords.top + coords.height};
			}
			else {
			// Align the menu as a side flyout menu
				pos = {x: (coords.left + coords.width) - self.options.Overlap, y: coords.top};
			}

			menu.setPosition(pos);
			menu.visible();
		});
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: GetStatus
	//-----------------------------------------------------------------------------------------------------------------------------
	GetStatus: function(itemID) {
		wfx.log("wfx.Menu.GetStatus");
		return $(itemID).Status;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: SetStatus
	//-----------------------------------------------------------------------------------------------------------------------------
	SetStatus: function(itemID, status, icon) {
		wfx.log("wfx.Menu.SetStatus:"+itemID);
		$(itemID).Status = status;
		$(itemID+"Status").src = wfx.IconUrl(icon);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Enable
	//-----------------------------------------------------------------------------------------------------------------------------
	Enable: function(itemID) {
		wfx.log("wfx.Menu.Enable:"+itemID);
		//$(itemID).Status = status;
		//$(itemID+"Status").src = wfx.IconUrl(icon);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Disable
	//-----------------------------------------------------------------------------------------------------------------------------
	Disable: function(itemID) {
		wfx.log("wfx.Menu.Disable:"+itemID);
		//this.Items[itemID].addEvent('click', function(ev) { alert('test'); ev.stop(); });//
	//	this.Items[itemID].removeEvents(['mouseover']);
	//	this.Items[itemID].setOpacity(0.5);
		this.Items[itemID].removeEvents();
	//	for(var e in this.Items[itemID].$events) {
	//		wfx.log("event:"+e);
	//	}
	}

	//-----------------------------------------------------------------------------------------------------------------------------
	//:: Select
	//-----------------------------------------------------------------------------------------------------------------------------
	//Select: function(menu) {
	//	wfx.log("wfx.Menu.Select");
	//	wfx.log("select: "+menu);//.getProperty('id'));
	//	var hs = this.headings.getElements('li');
	//	if($type(menu) == 'string') {
		// We need to find the menu object by name or id
	//		for(var i=0; i<hs.length; i++) {
	//			wfx.log("li: "+hs[i].id);
	//			if(hs[i].id == menu || hs[i].innerHTML == menu) {
	//				menu = hs[i];
	//				break;
	//			}
	//		}
	//	}

		// loop through all the menu and deselect all but the one
	//	hs.each(
	//		function(li) {
	//			wfx.log("li: "+li.getProperty('id'));
	//			li.setProperty('class', (li == menu) ? 'selected' : 'deselect');
	//		}
	//	);
	//
	//	// loop through all the content and hide all but the selected
	//	var c = $(menu.getProperty('id')+"C");
	//	this.contents.getElements('li').each(
	//		function(li) {
	//			wfx.log("li: "+li.getProperty('id'));
	//			li.setStyle('display', (li == c) ? 'block' : 'none');
	//		}
	//	);
	//}

}); // Menu

wfx.LoginData = null;
//-----------------------------------------------------------------------------------------------------------------------------
//: Login
//-----------------------------------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Login = function(container, data, session) {
	wfx.log("wfx.Login("+container+", "+data+", "+session+")");

	wfx.LoginData = eval(data);
	wfx.LoginData.Container = $(container);

	wfx.LoginData.Message = $(document.createElement('div'));
	wfx.LoginData.Message.innerHTML = "<b>You are currently logged in as:</b><br>";

	wfx.LoginData.Contents = $(document.createElement('div'));

	wfx.LoginData.Container.appendChild(wfx.LoginData.Message);
	wfx.LoginData.Container.appendChild(wfx.LoginData.Contents);

	if(wfx.LoginData.UserAccess) {
		wfx.log("loggedIn");
		wfx.LoginInfo();
	}
	else {
		wfx.LoginForm();
	}

}; // Login

//-----------------------------------------------------------------------------------------------------------------------------
//: LoginInfo
//-----------------------------------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------------------------------
wfx.LoginInfo = function() {
	wfx.log("wfx.LoginInfo");

	wfx.LoginData.Message.innerHTML = "<b>Please login</b><br>";

	f = new wfx.Form('loginInfo', {Validate:wfx.LoginValidate});
	f.AddRow("Login", wfx.LoginData.Login);
	f.AddRow("Access", wfx.LoginData.UserAccess);
	f.AddButton("cmdReLogin", 	{Label:'Re-Login', Submit:false, Action:wfx.LoginForm},		{});

	wfx.LoginData.Contents.empty();
	wfx.LoginData.Contents.appendChild(f);
}; // LoginInfo

//-----------------------------------------------------------------------------------------------------------------------------
//: LoginForm
//-----------------------------------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------------------------------
wfx.LoginForm = function() {
	wfx.log("wfx.LoginForm");

	wfx.LoginData.Message.innerHTML = "<b>Please login</b><br>";

	f = new wfx.Form('loginForm', {Validate:wfx.LoginValidate, UseAjax:true});

	f.AddInput('LOGIN', 			{Label:'Login'}, 		{type:'text', value:wfx.LoginData.Login});
	f.AddInput('PASSWORD', 		{Label:'Password', Submit:'cmdDoLogin'}, 	{type:'password', value:""});
	f.AddButton("cmdDoLogin", 	{Label:'Login', Submit:true},		{});
	f.AddButton("cmdLoginCancel", 	{Label:'Cancel'},		{});

	wfx.LoginData.Contents.empty();
	wfx.LoginData.Contents.appendChild(f);
}; // LoginForm

//-----------------------------------------------------------------------------------------------------------------------------
//: LoginValidate
//-----------------------------------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------------------------------
wfx.LoginValidate = function(form, cmd) {
	wfx.log("wfx.Login.LoginValidate");

	var valid = true;
	if(cmd == "cmdDoLogin") {
		if(!form.GetValue('LOGIN')) {
			wfx.log("No login");
			valid = false;
			$('loginMessage').innerHTML = "<div class='warning'>Please enter your login name</div>";
		}
		else
		if(!form.GetValue('PASSWORD')) {
			wfx.log("No password");
			valid = false;
			$('loginMessage').innerHTML = "<div class='warning'>Please enter your password</div>";
		}
	}

	return valid;
}; // LoginValidate

//-----------------------------------------------------------------------------------------------------------------------------
//: Debug
//-----------------------------------------------------------------------------------------------------------------------------
wfx.Debug = {
	LastID:null,
	LastErrorID:null,
	Win:null,
	Count:0,

	//-----------------------------------------------------------------------------------------------------------------------------
	// Init
	//-----------------------------------------------------------------------------------------------------------------------------
	Init: function(re) {
		wfx.log("wfx.Debug.Init");

		$$('.dbgBug').each(
			function(d) {
				d.setProperty('href', "javascript:wfx.Debug.Show('"+d.id+"');");
			}
		);
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	// Add
	//-----------------------------------------------------------------------------------------------------------------------------
	Add: function(content) {
		wfx.log("wfx.Debug.Add");
		wfx.Debug.Count++;
		var id = 'dbg'+wfx.Debug.Count;
		b = new wfx.Button(id, {Icon:'bug', Label:'', Action:wfx.Debug.Show.bind(this, id), Style:'dbgBug'});
		wfx.App.Page.appendChild(b);
		var c = wfx.App.Page.addElement('div', {id:id+'Data', 'class':'hidden'});
		c.innerHTML = content;
	},
	//-----------------------------------------------------------------------------------------------------------------------------
	// Show
	//-----------------------------------------------------------------------------------------------------------------------------
	Show: function(id) {
		wfx.log("wfx.Debug.Show");
		var v = false;
		if(!wfx.Debug.Win) {
			wfx.Debug.Win = new wfx.Window('dbg', {Title:'Debug'});
			v = wfx.Debug.Win.Views.Add('Main');
			wfx.Debug.Win.Show();
		}
		if(wfx.Debug.LastID == id) {
			wfx.Debug.Win.Toggle();
		}
	 	wfx.Debug.Win.SetTitle(id);

		if(!v) v = wfx.Debug.Win.Views.Get('Main');
	 	v.innerHTML = $(id+"Data").innerHTML;
	 	wfx.Debug.LastID = id;
	}
}; // Debug



