﻿/*
* fui.field.js 
*
* Fusion 3.0 UI Field Widget
*
*/

// FCK Editor List (needs to be global)
FCKeditors_Loaded = {};

function FCKeditor_OnComplete(editorInstance) {
	editorInstance.Events.AttachEvent('OnAfterSetHTML', FCKeditor_OnAfterSetHTML);
	FCKeditors_Loaded[editorInstance.Name] = editorInstance;
}

function FCKeditor_OnAfterSetHTML(editorInstance) {
}

(function($) {

	$.widget("fui.field", {

		_init: function() {

			// Parse inline attributes
			var attr = this.element[0].attributes, inline = {}, l = this.element[0].attributes.length;
			for (var i = 0; i < l; i++) {
				if (attr[i].name.indexOf("field:") == 0)
					inline[attr[i].name.toLowerCase()] = attr[i].value;
			}
			if (inline["field:fieldtype"]) this.options.fieldType = inline["field:fieldtype"];
			if (inline["field:datafield"]) this.options.dataField = inline["field:datafield"];
			if (inline["field:outputfield"]) this.options.outputField = inline["field:outputfield"];
			if (inline["field:group"]) this.options.group = inline["field:group"];

			if (inline["field:iseditable"]) this.options.isEditable = inline["field:iseditable"] !== "false";
			if (inline["field:isupdateable"]) this.options.isUpdateable = inline["field:isupdateable"] === "true";
			if (inline["field:isinsertable"]) this.options.isInsertable = inline["field:isinsertable"] === "true";
			if (inline["field:isdisabled"]) this.options.isDisabled = inline["field:isdisabled"] === "true";
			if (inline["field:warnonleave"]) this.options.warnOnLeave = inline["field:warnonleave"] === "true";

			if (inline["field:isnullable"]) this.options.isNullable = inline["field:isnullable"] === "true";
			if (inline["field:isrequired"]) this.options.isRequired = inline["field:isrequired"] === "true";

			if (inline["field:min"]) this.options.min = parseFloat(inline["field:min"]);
			if (inline["field:max"]) this.options.max = parseFloat(inline["field:max"]);

			if (inline["field:tabindex"]) this.options.tabIndex = parseInt(inline["field:tabindex"] || "0");
			if (inline["field:header"]) this.options.header = inline["field:header"];
			if (inline["field:width"]) this.options.width = inline["field:width"];
			if (inline["field:minwidth"]) this.options.minWidth = inline["field:minwidth"];
			if (inline["field:height"]) this.options.height = inline["field:height"];
			if (inline["field:align"]) this.options.align = inline["field:align"];
			if (inline["field:ispassword"]) this.options.isPassword = inline["field:ispassword"];
			if (inline["field:watermark"]) this.options.watermark = inline["field:watermark"];
			if (inline["field:prefix"]) this.options.prefix = inline["field:prefix"];

			if (inline["field:projectiondatasource"]) this.options.projectionDataSource = inline["field:projectiondatasource"];
			if (inline["field:projectionname"]) this.options.projectionName = inline["field:projectionname"];
			if (inline["field:namefield"]) this.options.nameField = inline["field:namefield"];
			if (inline["field:valuefield"]) this.options.valueField = inline["field:valuefield"];
			if (inline["field:nullname"]) this.options.nullName = inline["field:nullname"];

			if (inline["field:value"]) {
				try { this.options.value = eval(inline["field:value"]); } catch (e) { }
				if (this.options.value === undefined) this.options.value = inline["field:value"];
			}
			if (inline["field:defaultvalue"]) {
				try { this.options.defaultValue = eval(inline["field:defaultvalue"]); } catch (e) { }
				if (this.options.value === undefined) this.options.value = inline["field:defaultvalue"];
			}
			if (inline["field:regex"]) {
				try { this.options.regex = eval(inline["field:regex"]); } catch (e) { }
			}
			if (inline["field:validchars"]) {
				try { this.options.validChars = eval(inline["field:validchars"]); } catch (e) { }
			}

			// Look for inline settings
			/*
			for (var attrName in $.fui.field.defaults) {
			var attrValue = this.element.attr("field:" + attrName);
			if (attrValue) {
			try {
			this.options[attrName] = eval(attrValue);
			if (this.options[attrName] === undefined)
			this.options[attrName] = attrValue;
			} catch (err) {
			this.options[attrName] = attrValue;
			}
			}
			}
			*/

			if (this.options.header == null) {
				if (this.options.dataField) {
					var header = "";
					var upper = null;
					// TODO: Strip off component names
					for (var i = 0; i < this.options.dataField.length; i++) {
						var c = this.options.dataField.substr(i, 1);
						if (!upper && (c === c.toUpperCase())) header += " ";
						header += c;
						upper = (c === c.toUpperCase());
					}
					header = header.replace(" Id", "");
				}
				this.options.header = header;
			}

			this.element.width(this.options.width);
			this.element.addClass("fui-field");
			$.extend(this, $.fui.field.plugins[this.options.fieldType.toLowerCase()]);
			if (this._initPlugin) this._initPlugin();
			this._setEditable(this.options.isEditable);
			this._setValue(this.options.defaultValue);
		},

		destroy: function() {
		},

		setValues: function(values, group, clear, changed) {
			values = values || {};
			if (group !== undefined && group !== this.options.group && this.options.group.indexOf(group + ":") !== 0) return;
			var property = this.options.dataField;
			if (values[property] !== undefined)
				this._setValue(values[property], changed);
			else if (clear === true)
				this._setValue(this.options.defaultValue, changed);
		},

		getValues: function(values, group) {
			if (group !== undefined && group !== this.options.group && this.options.group.indexOf(group + ":") !== 0) return;
			var tmp = this.validate();
			if (tmp) {
				if (!$.validationError) $.validationError = tmp + "\n";
				else $.validationError = $.validationError + tmp + "\n";
			}
			var value = this._getValue();
			if (value === undefined) return;
			var property = this.options.outputField || this.options.dataField;
			values[property] = value;
		},

		unsavedChanges: function(fields) {
			if (this.options.warnOnLeave === true && (this.element.hasClass("cgd") || this.element.hasClass("ivd")))
				fields.push(this);
		},

		getFilters: function(finqNodes, group) {
			if (group !== undefined && group !== this.options.group && this.options.group.indexOf(group + ":") !== 0) return;
			var node = this.getFilter();
			if (node) finqNodes.push(node);
		},

		validateRadioFields: function(values, group) {
			if (group !== undefined && group !== this.options.group && this.options.group.indexOf(group + ":") !== 0) return;
			if (this.options.fieldType == "radio" && !this.options.isNullable) {
				if (values[this.options.dataField] === undefined || values[this.options.dataField] === null) {
					var tmp = this.options.header + " cannot be blank.  A value is required.\n";
					if (!$.validationError) $.validationError = tmp;
					else if ($.validationError.indexOf(tmp) == -1) $.validationError = $.validationError + tmp;
				}
			}
		},

		_setData: function(key, value) {
			if (this.options[key] === value && key != 'value')
				return;
			if (key == 'isEditable') {
				this._setEditable(value);
			} if (key == 'value') {
				this._setValue(value);
			} else {
				this.options[key] = value;
			}
		},

		_getData: function(key) {
			if (key == 'value') {
				return this._getValue();
			} else {
				return this.options[key];
			}
		},

		_setEditable: function(isEditable) {
			var value = this._getValue();
			$(this._editElement)[isEditable ? "show" : "hide"]();
			$(this._viewElement)[isEditable ? "hide" : "show"]();
			this.options.isEditable = isEditable;
			this._setValue(value);
		},

		_styleElement: function(element, allowOverflow) {
			if(!allowOverflow)
				element.style.overflow = 'hidden';
			else
				element.style.overflow = 'auto';

			element.style.width = this.options.width;
			element.style.minWidth = this.options.minWidth;
			element.style.height = this.options.height;
			element.style.textAlign = this.options.align;
			element.tabIndex = this.options.tabIndex;
			element.disabled = this.options.isDisabled;
		},

		// Default - works for text fields (i.e. text, textarea, html) - override for other fields
		validate: function() {
			this.element.removeClass("ivd");
			var value = this._getValue();
			if (value == null && this.options.isNullable) return;
			if (!value || value == '_blank_') value = '';
			this.element.addClass("ivd");
			if ((isFinite(this.options.min) && value.length < this.options.min) || (this.options.isRequired && value.length == 0)) {
				if (value.length == 0) return this.options.header + " cannot be blank.  A value is required.";
				return this.options.header + " must be at least " + this.options.min + " characters long.";
			}
			if (isFinite(this.options.max) && value.length > this.options.max) {
				return this.options.header + " cannot be more than " + this.options.max + " characters long.";
			}
			if (value.length > 0 && this.options.regex && !this.options.regex.test(value)) {
				if (this.options.regexMessage) return this.options.regexMessage;
				return this.options.header + " is not in a valid format.";
			}
			if (value.length > 0 && this.options.validChars && !this.options.validChars.test(value)) {
				if (this.options.validCharsMessage) return this.options.validCharsMessage;
				return this.options.header + " contains invalid characters.";
			}
			this.element.removeClass("ivd");
		},

		// Default - works for text fields (i.e. text, textarea, html?) - override for other fields
		getFilter: function() {
			var value = this._getValue();
			if (!value || value.length == 0) return;
			if (value.indexOf('=') == 0) return $.finqNode(this.options.dataField, value.substring(1), $.finqNodeTypes.Like);
			if (value.indexOf('%') > -1) return $.finqNode(this.options.dataField, value, $.finqNodeTypes.Like);
			return $.finqNode(this.options.dataField, '%' + value + '%', $.finqNodeTypes.Like);
		},

		updateMode: function() {
			this._setEditable(this.options.isUpdateable);
		},

		insertMode: function() {
			this._setEditable(this.options.isInsertable);
		},

		filterMode: function() {
		if (this.options.fieldType == 'text' || this.options.fieldType == 'textarea' || this.options.fieldType == 'boolean' || this.options.fieldType == 'dropdownlist' || this.options.fieldType == 'date' || this.options.fieldType == 'decimal' || this.options.fieldType == 'integer')
				this._setEditable(true);
			else
				this._setEditable(false);
			this.options.isNullable = true;
			this.options.defaultValue = null;
			this._setValue(null);
		}
	});

	$.extend($.fui.field, {
		eventPrefix: "field",
		defaults: {
			fieldType: "text",
			dataField: null,
			outputField: null,
			group: '', // TODO: Make 'DataGroup?'
			value: null,
			defaultValue: null,
			// <<< Configuration >>>
			isEditable: true,
			isInsertable: false, // TODO: modes?
			isUpdateable: false,
			isDisabled: false,
			warnOnLeave: false, // TODO: isCheckedOnLeave? or something??
			config: null, // Arbitrary config string (e.g. URL of editor config file)
			// <<< Validation >>>
			isNullable: false,
			isRequired: false,
			min: NaN,
			max: NaN,
			regex: null,
			validChars: null,
			// <<< Presentation >>>
			tabIndex: 0,
			header: null,
			width: '100%',
			minWidth: null,
			height: null,
			align: '',
			isPassword: false,
			watermark: null,
			prefix: null,
			// <<< Projection >>>
			projectionDataSource: null,
			projectionName: null,
			nameField: 'Name',
			valueField: 'Id',
			nullName: ""
		}
	});

	// All the standard field type plugins (you can add more!)
	$.extend($.fui.field, {
		plugins: {
			"text": {
				_initPlugin: function() {
					this._styleElement(this._viewElement = document.createElement('span'));
					this._styleElement(this._editElement = document.createElement('input'));
					if (this.options.isPassword) this._editElement.type = "password";
					else this._editElement.type = "text";
					if (isFinite(this.options.max)) this._editElement.maxLength = this.options.max;
					$(this._editElement).focus($.delegate(this._onFocus, this));
					$(this._editElement).blur($.delegate(this._onBlur, this));
					$(this._editElement).keydown($.delegate(this._onKeydown, this));
					$(this._editElement).keypress($.delegate(this._onKeypress, this));
					this.element.append(this._viewElement).append(this._editElement);
				},

				_setValue: function(value, changed) {
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd').removeClass('wmk');
					if (typeof (value) !== 'string') { value = this.options.defaultValue || ''; }
					if (this.options.isEditable) {
						if ((value == '' || value == '_blank_') && this.options.watermark) {
							this._editElement.value = this.options.watermark;
							this.element.addClass('wmk');
						} else {
							this._editElement.value = value;
						}
					} else {
						if (!value || value == '') value = '&nbsp;';
						var _value = (this.options.prefix || '') + value;
						$(this._viewElement).html(_value);
						this._viewElement.title = _value;
					}
				},

				_getValue: function(value) {
					if (this.options.isEditable) {
						if (this.element.hasClass("wmk")) return this.options.isNullable ? null : (this.options.isRequired || isFinite(this.options.min) ? '_blank_' : '');
						var value = this._editElement.value;
					} else {
						var value = $(this._viewElement).text();
					}
					return this.options.isNullable && value == '' ? null : value;
				},

				_onKeydown: function(event) {
					if (this._trigger("keypress", event) === false) return;
					this.element.removeClass("ivd").addClass("cgd");
					if ((event.keyCode || event.charCode) == 13) return this._editElement.blur();
				},

				_onKeypress: function(event) {
					if (this.options.validChars) {
						if (!this.options.validChars.test(String.fromCharCode((event.keyCode || event.charCode))))
							event.preventDefault();
					}
				},

				_onFocus: function(event) {
					if (this.element.hasClass("wmk")) {
						this._editElement.value = "";
						this.element.removeClass("wmk");
					}
				},

				_onBlur: function(event) {
					if (this.options.watermark) {
						var value = this._getValue();
						if (value == null || value == '' || value == '_blank_') {
							this._editElement.value = this.options.watermark;
							this.element.addClass('wmk');
						}
					}
				}

			},

			"textarea": {
				_initPlugin: function() {
					this._styleElement(this._viewElement = document.createElement('span'));
					this._styleElement(this._editElement = document.createElement('textarea'), true);
					$(this._editElement).focus($.delegate(this._onFocus, this));
					$(this._editElement).blur($.delegate(this._onBlur, this));
					$(this._editElement).keydown($.delegate(this._onKeydown, this));
					this.element.append(this._viewElement).append(this._editElement);
				},

				_setValue: function(value, changed) {
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd').removeClass('wmk');
					if (typeof (value) !== 'string') { value = this.options.defaultValue || ''; }
					if (this.options.isEditable) {
						if ((value == '' || value == '_blank_') && this.options.watermark) {
							this._editElement.value = this.options.watermark;
							this.element.addClass('wmk');
						} else {
							this._editElement.value = value;
						}
					} else {
						$(this._viewElement).text(value);
					}
				},

				_getValue: function(value) {
					if (this.options.isEditable) {
						if (this.element.hasClass("wmk")) return this.options.isNullable ? null : (this.options.isRequired || isFinite(this.options.min) ? '_blank_' : '');
						var value = this._editElement.value;
					} else {
						var value = $(this._viewElement).text();
					}
					return this.options.isNullable && value == '' ? null : value;
				},

				_onKeydown: function(event) {
					if (this._trigger("keypress", event) === false) return;
					this.element.removeClass("ivd").addClass("cgd");
				},

				_onFocus: function(event) {
					if (this.element.hasClass("wmk")) {
						this._editElement.value = "";
						this.element.removeClass("wmk");
					}
				},

				_onBlur: function(event) {
					if (this.options.watermark) {
						var value = this._getValue();
						if (value == null || value == '' || value == '_blank_') {
							this._editElement.value = this.options.watermark;
							this.element.addClass('wmk');
						}
					}
				}
			},

			"date": {
				_initPlugin: function() {
					this._styleElement(this._viewElement = document.createElement('span'));
					this._styleElement(this._editElement = document.createElement('input'));
					$(this._editElement).keydown($.delegate(this._onKeydown, this));
					$(this._editElement).blur($.delegate(this._onBlur, this));
					$(this._editElement).datepicker({ dateFormat: 'dd-M-yy', changeMonth: true, changeYear: true });
					this.element.append(this._viewElement).append(this._editElement);
				},

				_setValue: function(value, changed) {
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd');
					value = $.parseDate(value);
					if (value == null) value = this.options.defaultValue || null;
					if (this.options.isEditable) {
						this._editElement.value = value == null ? (this.options.watermark || '') : $.formatDate(value);
					} else {
						var _value;
						if (value == null) _value = this.options.watermark || '&nbsp;';
						else _value = (this.options.prefix || '') + $.formatDate(value);
						$(this._viewElement).html(_value);
					}
				},

				_getValue: function(value) {
					if (this.options.isEditable) {
						return $.parseDate(this._editElement.value);
					} else {
						return $.parseDate($(this._viewElement).text());
					}
				},

				_onKeydown: function(event) {
					if (this._trigger("keypress", event) === false) return;
					if ((event.keyCode || event.charCode) == 13) {
						this._editElement.blur();
					}
					this.element.removeClass("ivd").addClass("cgd");
				},

				_onBlur: function(event) {
					var date = $.parseDate(this._editElement.value);
					this._editElement.value = date == null ? '' : $.formatDate(date);
				},

				validate: function() {
					if (this._getValue() == null && !this.options.isNullable) {
						this.element.removeClass("cgd").addClass("ivd");
						return this.options.header + " must be a valid date.";
					}
				},

				getFilter: function() {
					var value = this._getValue();
					if (value == null) return;
					return $.finqNode(this.options.dataField, value, $.finqNodeTypes.Equal);
				}
			},

			"decimal": {
				_initPlugin: function() {
					this._styleElement(this._viewElement = document.createElement('span'));
					this._styleElement(this._editElement = document.createElement('input'));
					$(this._editElement).keypress($.delegate(this._onKeypress, this));
					$(this._editElement).keydown($.delegate(this._onKeydown, this));
					$(this._editElement).blur($.delegate(this._onBlur, this));
					this.element.append(this._viewElement).append(this._editElement);
				},

				_setValue: function(value, changed) {
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd');
					if (!isFinite(value)) value = this.option.defaultValue;
					if (this.options.isEditable) {
						this._editElement.value = (value == null) ? '' : $.formatNumber(value, 2);
					} else {
						var _value = (this.options.prefix || '') + ((value == null) ? '&nbsp;' : $.formatNumber(value, 2));
						$(this._viewElement).html(_value);
					}
				},

				_getValue: function() {
					if (this.options.isEditable) {
						var value = parseFloat(this._editElement.value);
						if (isNaN(value)) return null;
						return value;
					} else {
						var value = parseFloat($(this._viewElement).text());
						if (isNaN(value)) return null;
						return value;
					}
				},

				_onKeypress: function(event) {
					// block all key codes not in '0-9', '-', '.'
					var keyCode = (event.keyCode || event.charCode);
					if ((keyCode < 48 || keyCode > 57) && keyCode != 45 && keyCode != 46 && keyCode != 8 && keyCode != 37 && keyCode != 39)
						return false;
					event.stopPropagation();
				},

				_onKeydown: function(event) {
					if (this._trigger("keypress", event) === false) return;
					this.element.removeClass("ivd").addClass("cgd");
					if ((event.keyCode || event.charCode) == 13) {
						this._editElement.blur();
					}
				},

				_onBlur: function(event) {
					var number = parseFloat(this._editElement.value);
					this._editElement.value = number == NaN ? '' : $.formatNumber(number, 2);
				},

				validate: function() {
					var value = this._getValue();
					this.element.addClass("ivd");
					if (value == null) {
						if (this.options.isNullable) { this.element.removeClass("ivd"); return; }
						return this.options.header + " is required.";
					}
					if (isFinite(this.options.min) && value < this.options.min) return this.options.header + " cannot be less than " + this.options.min;
					if (isFinite(this.options.max) && value > this.options.max) return this.options.header + " cannot be greater than " + this.options.max;
					this.element.removeClass("ivd");
				},

				getFilter: function() {
					var value = this._getValue();
					if (value == null) return;
					return $.finqNode(this.options.dataField, value, $.finqNodeTypes.Equal);
				}
			},

			"integer": {
				_initPlugin: function() {
					this._styleElement(this._viewElement = document.createElement('span'));
					this._styleElement(this._editElement = document.createElement('input'));
					$(this._editElement).keypress($.delegate(this._onKeypress, this));
					$(this._editElement).keydown($.delegate(this._onKeydown, this));
					$(this._editElement).blur($.delegate(this._onBlur, this));
					this.element.append(this._viewElement).append(this._editElement);
				},

				_setValue: function(value, changed) {
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd');
					if (!isFinite(value)) value = this.options.defaultValue;
					if (this.options.isEditable) {
						this._editElement.value = (value == null) ? '' : $.formatNumber(value, 0);
					} else {
						var _value = (this.options.prefix || '') + ((value == null) ? '&nbsp;' : $.formatNumber(value, 0));
						$(this._viewElement).html(_value);
					}
				},

				_getValue: function() {
					if (this.options.isEditable) {
						var value = parseInt(this._editElement.value);
						if (isNaN(value)) return null;
						return value;
					} else {
						var value = parseInt($(this._viewElement).text());
						if (isNaN(value)) return null;
						return value;
					}
				},

				_onKeypress: function(event) {
					// block all key codes not in '0-9', '-'
					var keyCode = event.keyCode || event.charCode;
					if ((keyCode < 48 || keyCode > 57) && keyCode != 45 && keyCode != 8 && keyCode != 37 && keyCode != 39)
						return false;
					event.stopPropagation();
				},

				_onKeydown: function(event) {
					if (this._trigger("keypress", event) === false) return;
					this.element.removeClass("ivd").addClass("cgd");
					if ((event.keyCode || event.charCode) == 13) {
						this._editElement.blur();
					}
				},

				_onBlur: function(event) {
					var number = parseFloat(this._editElement.value);
					this._editElement.value = number == NaN ? '' : $.formatNumber(number, 0);
				},

				validate: function() {
					var value = this._getValue();
					this.element.addClass("ivd");
					if (value == null) {
						if (this.options.isNullable) { this.element.removeClass("ivd"); return; }
						return this.options.header + " is required.";
					}
					if (isFinite(this.options.min) && value < this.options.min) return this.options.header + " cannot be less than " + this.options.min;
					if (isFinite(this.options.max) && value > this.options.max) return this.options.header + " cannot be greater than " + this.options.max;
					this.element.removeClass("ivd");
				},

				getFilter: function() {
					var value = this._getValue();
					if (value == null) return;
					return $.finqNode(this.options.dataField, value, $.finqNodeTypes.Equal);
				}

			},

			"editor": {
				_initPlugin: function() {
					this.element.addClass("fck-frame");
					this.element[0].style.display = "block";
					this.element[0].style.overflow = "hidden";
					this.fck = new FCKeditor($.stamp(this));
					this.fck.BasePath = "/fusion/ckeditor/";
					if (this.options.config) {
						this.fck.Config["CustomConfigurationsPath"] = this.options.config;
					}
					this.fck.Width = this.options.width;
					this.fck.Height = this.options.height;
					this.element.html(this.fck.CreateHtml());
				},

				_setEditable: function(isEditable) {
					this.options.isEditable = isEditable;
				},

				_setValue: function(value, changed) {
					if (value === null || value === undefined) value = "";
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd');
					var fck = FCKeditors_Loaded[$.stamp(this)];
					if (fck !== undefined)
						fck.SetHTML(value);
				},

				_getValue: function() {
					var fck = FCKeditors_Loaded[$.stamp(this)];
					if (fck !== undefined)
						return fck.GetHTML();
				},

				validate: function() { },

				getFilter: function() { }
			},

			"image": {
				_initPlugin: function() {
					this.element.append(this._editElement = document.createElement('img'));
					this._editElement.alt = "\n(Double click to change)";
					this.element.dblclick($.delegate(this._dblclick, this));

				},

				_setEditable: function(isEditable) {
					this.options.isEditable = isEditable;
				},

				_setValue: function(value, changed) {
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd');
					if (value) {
						this._editElement.src = value;
						this._editElement.alt = value + "\n(Double click to change)";
					} else {
						this._editElement.src = "";
						this._editElement.alt = "No image has been set.\n(Double click to change)";
					}
				},

				_getValue: function() {
					// TODO: Strip off any domain name?
					return this._editElement.src;
				},

				_dblclick: function(event) {
					var self = this;
					window['_imageFieldCallback'] = function(url) { self._setValue(url); self.element.addClass('cgd'); };
					CKFinder.Popup('/fusion/ckfinder', null, null, '_imageFieldCallback', 'Images');
				},

				validate: function() { },

				getFilter: function() { }
			},

			"radio": {
				_initPlugin: function() {
					this._editElement = document.createElement('span');
					this._editElement.style.margin = 'auto';
					this._editElement.style.display = 'block'; // TODO: Can we remove this fudge!?
					$(this._editElement).addClass("ui-icon").click($.delegate(this._click, this));
					$(this.element).append(this._editElement);
					this._value = this.options.defaultValue;
					this._refresh();
				},

				_setEditable: function(isEditable) {
					this.options.isEditable = isEditable;
				},

				_setValue: function(value, changed) {
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd');
					this._value = (value === this.options.value);
					this._refresh();
				},

				_getValue: function() {
					if (this._value) return this.options.value;
					return undefined;
				},

				_refresh: function() {
					$(this._editElement).removeClass("ui-icon-radio-on ui-icon-radio-off");
					if (this._value) {
						$(this._editElement).addClass("ui-icon-radio-on");
					} else {
						$(this._editElement).addClass("ui-icon-radio-off");
					}
				},

				_click: function() {
					if (!this.options.isEditable) return;
					var values = {};
					values[this.options.dataField] = this.options.value;
					$(".fui-field").field("setValues", values);
					this._refresh();
					this._trigger("changed", event);
				},

				validate: function() { },

				getFilter: function() { }

			},

			"boolean": {
				_initPlugin: function() {
					this._editElement = document.createElement('span');
					this._editElement.style.display = 'block'; // TODO: Can we remove this fudge!?
					$(this._editElement).addClass("ui-icon").click($.delegate(this._click, this));
					$(this.element).append(this._editElement);
					this._value = this.options.defaultValue;
					this._refresh();
				},

				_setEditable: function(isEditable) {
					this.options.isEditable = isEditable;
				},

				_setValue: function(value, changed) {
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd');
					this._value = value;
					this._refresh();
				},

				_getValue: function() {
					if (this._value === true || this._value === false) return this._value;
					return null;
				},

				_refresh: function() {
					if (this._value !== true && this._value !== false && !this.options.isNullable) this._value = true;
					$(this._editElement).removeClass("ui-icon-checked ui-icon-unchecked ui-icon-maybechecked");
					if (this._value === true) {
						$(this._editElement).addClass("ui-icon-checked");
					} else if (this._value === false) {
						$(this._editElement).addClass("ui-icon-unchecked");
					} else {
						$(this._editElement).addClass("ui-icon-maybechecked");
					}
				},

				_click: function() {
					if (!this.options.isEditable) return;
					$(this.element).removeClass("ivd").addClass("cgd");
					if (this._value === true) {
						this._value = false;
					} else if (this._value === false && this.options.isNullable) {
						this._value = null;
					} else {
						this._value = true;
					}
					this._refresh();
					this._trigger("changed", event);
				},

				validate: function() { },

				getFilter: function() {
					var value = this._getValue();
					if (value === null) return;
					if (value) return $.finqNode(this.options.dataField, 1, $.finqNodeTypes.Equal);
					return $.finqNode(this.options.dataField, 0, $.finqNodeType.Equal);
				}
			},

			"dropdownlist": {
				_initPlugin: function() {
					// Usual Styling Stuff (can we factor out?)
					this._styleElement(this._viewElement = document.createElement('span'));
					this._styleElement(this._editElement = document.createElement('select'));
					$(this._editElement).change($.delegate(this.change, this));
					this.element.append(this._viewElement).append(this._editElement);
					$(this._editElement).keypress($.delegate(this._keypress, this));
					this._value = this.options.defaultValue;
					this._populate();
				},

				_setValue: function(value, changed) {
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd');
					if (this.options.isEditable) {
						this._value = value;
						if (!this._options) return;
						this._editElement.value = (value === null) ? '' : value;
					} else {
						this._value = value;
						if (!this._options) return;
						$(this._viewElement).text(this._lookup(value));
					}
				},

				_getValue: function() {
					if (this.options.isEditable) {
						if (!this._options) { return this._value; }
						var value = this._editElement.value;
						if (value === "") value = null;
						else if (typeof (value) == "string") value = parseInt(value, 10);
						return value;
					} else {
						return this._value;
					}
				},

				_populate: function() {
					if (this._populating) {
						return;
					}
					this._populating = true;
					// TODO: Recover on error?
					$PageManager.getDataSource(this.options.projectionDataSource).selectProjection(this.options.projectionName, $.delegate(this._populateCallback, this));
				},

				_populateCallback: function(sender, eventArgs) {
					var options = eventArgs.projection;
					this._editElement.options.length = 0;
					var n = 0;
					if (this.options.isNullable) {
						this._editElement.options[0] = new Option(this.options.nullName, '', false, false);
						n = 1;
					}
					for (var i = 0; i < options.length; i++) {
						this._editElement.options[i + n] = new Option(options[i][this.options.nameField], options[i][this.options.valueField], false, false);
					}
					this._options = options;
					if (this._value !== undefined)
						this._setValue(this._value);
					delete this._populating;
				},

				_lookup: function(value) {
					if (value == null) return this.options.nullName;
					if (!this._options) return '';
					for (var i = 0; i < this._options.length; i++) {
						if (this._options[i][this.options.valueField] == value)
							return this._options[i][this.options.nameField];
					}
					return '';
				},

				_keypress: function(event) {
					if (this._trigger("keypress", event) === false) {
						event.preventDefault();
						event.stopPropagation();
					}
				},

				change: function(event) {
					this.element.addClass('cgd');
					this._trigger("changed", event);
				},

				validate: function() {
					this.element.removeClass('cgd').removeClass('ivd');
					var value = this._getValue();
					if (value === null && (!this.options.isNullable || this.options.isRequired)) return this.options.header + " cannot be left blank.";
				},

				getFilter: function() {
					var value = this._getValue();
					if (!value || value === '' || value === 'null') return;
					return $.finqNode(this.options.dataField, value, $.finqNodeTypes.Equal);
				}

			},

			// TODO: Fix for Fusion 4.0 / Projections
			"combobox": {
				_initPlugin: function() {
					this._styleElement(this._viewElement = document.createElement('span'));
					this._styleElement(this._editElement = document.createElement('input'));
					$(this._editElement).addClass("fui-combobox");
					$(this._editElement).keydown($.delegate(this._keydown, this));
					$(this._editElement).keypress($.delegate(this._keypress, this));
					$(this._editElement).focus($.delegate(this._focus, this));
					$(this._editElement).blur($.delegate(this._blur, this));
					$(this._editElement).mousedown($.delegate(this._mousedown, this));

					this.element.append(this._viewElement).append(this._editElement);
					this._value = null;
					this._selectedIndex = -1;
				},

				_setValue: function(value, changed) {
					if (!changed) this.element.removeClass('cgd'); else this.element.addClass('cgd');
					this.element.removeClass('ivd');
					this._value = value;
					if (value == null)
						this._editElement.value = "";
					else
					// Do a lookup based on current value
						$.lookup(this.options.key, $.delegate(this._lookupCallback, this));
				},

				_getValue: function(value) {
					return this._value;
				},

				_initPopup: function(popupElement) {
					popupElement.click($.delegate(this._clickPopup, this));
					popupElement.focus($.delegate(this._focusPopup, this));
					popupElement.blur($.delegate(this._blurPopup, this));
					popupElement.mousemove($.delegate(this._mousemovePopup, this));
					popupElement.addClass("fui-popup-autocomplete");
					this._popupElement = popupElement;
					this._popupElement[0].scrollTop = 0;
				},

				_showPopup: function() {
					this._selectedIndex = -1;
					this._dontFilter = true;
					$.popup({ callback: $.delegate(this._initPopup, this), target: $(this._editElement), modal: false, closed: $.delegate(this._hidePopup, this) });
				},

				_hidePopup: function() {
					if (this._popupElement) {
						this._popupElement.unbind("click");
						this._popupElement.unbind("focus");
						this._popupElement.unbind("blur");
						this._popupElement.unbind("mousemove");
						this._popupElement = undefined;
						$.hidePopup();
						// Do a reverse lookup based on current contents
						$.lookup(this.options.key, $.delegate(this._reverseLookupCallback, this));
					}
				},

				// Call this to start a callback to the server based on what's currently in the text box
				_populate: function() {
					if (this._populating)
						return; // Already doing a callback so can't start another
					this._populating = true;
					$.lookup(this.options.key, $.delegate(this._populateCallback, this));
				},

				/* Texbox Events */

				_keydown: function(event) {
					if ((event.keyCode || event.charCode) === 27) { // esc
						this._hidePopup();
						return;
					}
					if ((event.keyCode || event.charCode) == 40) { // down arrow
						if (this._popupElement) {
							if (this._selectedIndex < this._popupElement[0].childNodes.length - 1) {
								this._highlightPopupItem(this._popupElement[0].childNodes[this._selectedIndex + 1]);
								this._handleScroll(this._popupElement[0].childNodes[0], this._selectedIndex);
							}
							return;
						}
					} else if ((event.keyCode || event.charCode) == 38) { // up arrow
						if (this._popupElement) {
							if (this._selectedIndex > 0) {
								this._highlightPopupItem(this._popupElement[0].childNodes[this._selectedIndex - 1]);
								this._handleScroll(this._popupElement[0].childNodes[0], this._selectedIndex);
							}
							return;
						}
					} else if ((event.keyCode || event.charCode) == 13) { // enter
						if (this._popupElement && this._selectedIndex != -1) {
							this._editElement.value = this._popupElement[0].childNodes[this._selectedIndex].innerHTML;
						}
						$.lookup(this.options.key, $.delegate(this._reverseLookupCallback, this));
						this._hidePopup();
						event.stopPropagation();
						// We dont prevent default here so that we can capture the keypress event
						return;
					} else if ((event.keyCode || event.charCode) == 9) { // tab
						if (this._popupElement && this._selectedIndex != -1) {
							this._editElement.value = this._popupElement[0].childNodes[this._selectedIndex].innerHTML;
						}
						$.lookup(this.options.key, $.delegate(this._reverseLookupCallback, this));
						this._hidePopup();
						return;
					}
					// Actual key pressed so try populating the popup based on the data
					this._dontFilter = false;
					setTimeout($.delegate(this._populate, this), 10);
				},

				_keypress: function(event) {
					if (this._trigger("keypress", event) === false) {
						event.stopPropagation();
						event.preventDefault();
					}
				},

				// Makes sure the currently selected popup item is in view
				_handleScroll: function(element, index) {
					var popup = this._popupElement[0];
					if (!popup) return;
					var numItems = popup.childNodes.length;
					var height = $(element).outerHeight(); // This isn't quite right!
					if (height * index - (popup.clientHeight + popup.scrollTop) >= 0) {
						// Scroll down
						popup.scrollTop += height * index - (popup.clientHeight + popup.scrollTop) + height;
					}
					if (height * (numItems - (index + 1)) - (popup.scrollHeight - popup.scrollTop) >= 0) {
						// Scroll up
						popup.scrollTop -= height * (numItems - (index + 1)) - (popup.scrollHeight - popup.scrollTop) + height;
					}
					if (popup.scrollTop % height !== 0) {
						if (height * (index + 1) - (popup.clientHeight + popup.scrollTop) >= 0) {
							// Element is partially displayed at the bottom
							popup.scrollTop -= popup.scrollTop % height;
						} else {
							// Element is partially displayed at the top
							popup.scrollTop += height - (popup.scrollTop % height);
						}
					}
				},

				_blur: function(event) {
					this._inputHasFocus = false;
					setTimeout($.delegate(this._handleInputBlur, this), 100);
				},

				_focus: function(event) {
					this._inputHasFocus = true;
					this._popupHasFocus = false;
					this._editElement.select();
					this._populate();
				},

				_handleInputBlur: function(event) {
					if (!this._popupHasFocus) {
						this._hidePopup();
					}
				},

				_mousedown: function(event) {
					if (this._inputHasFocus === true) {
						this._editElement.blur();
					}
				},

				/* Lookup Callbacks */

				_reverseLookupCallback: function(options) {
					this.element.addClass('cgd');
					var filter = this._editElement.value.toLowerCase().replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ');
					for (var i = 0; i < options.length; i++) {
						var test = options[i][this.options.nameField].toLowerCase().replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ');
						if (test == filter) {
							this._value = options[i].Value;
							this._editElement.value = options[i][this.options.nameField];
							return;
						}
					}
					this._value = null;
					this._editElement.value = "";
				},

				_lookupCallback: function(options) {
					var name = "";
					for (var i = 0; i < options.length; i++) {
						if (options[i].Value == this._value) {
							name = options[i][this.options.nameField];
							break;
						}
					}
					if (this.options.isEditable)
						this._editElement.value = name;
					else
						$(this._viewElement).text(name);
				},

				// This is called back by the server when the data is returned (which could be immediately)
				_populateCallback: function(options) {
					if (options.length > 0) {
						this._options = options;
						if (!this._popupElement) this._showPopup();
						this._popupElement.empty();
						var filter = this._editElement.value.toLowerCase();
						// Split the filter by spaces
						var parts = filter.split(' ');
						for (var i = 0; i < this._options.length; i++) {
							var fail = false;
							if (!this._dontFilter) {
								for (var j = 0; j < parts.length; j++) {
									if (this._options[i][this.options.nameField].toLowerCase().indexOf(parts[j]) < 0) {
										fail = true;
										break;
									}
								}
							}
							if (fail) continue;

							this._popupElement.append("<p>" + this._options[i][this.options.nameField] + "</p>");
							if (!this._showAll && i == 50) {
								this._popupElement.append("<p>[... show all]</p>");
								break;
							}
						}
					} else {
						this._hidePopup();
					}
					this._populating = false;
				},

				/* Popup Events */

				_clickPopup: function(event) {
					if (event.target.innerHTML == "[... show all]") {
						this._showAll = true;
						this._populate();
					}
					this._editElement.value = event.target.innerHTML;
					$.lookup(this.options.key, $.delegate(this._reverseLookupCallback, this));
					this._hidePopup();
				},

				_mousemovePopup: function(event) {
					var item = event.target;
					if (item != this._popupElement) {
						this._highlightPopupItem(item);
					}
				},

				_focusPopup: function(event) {
					this._popupHasFocus = true;
					this._inputHasFocus = false;
				},

				_blurPopup: function(event) {
					this._popupHasFocus = false;
					setTimeout($.delegate(this._handlePopupBlur, this), 100);
				},

				_handlePopupBlur: function(event) {
					if (!this._inputHasFocus) {
						this._hidePopup();
					}
				},

				_highlightPopupItem: function(item) {
					if (!this._popupElement) return;
					var children = this._popupElement[0].childNodes;
					// Unhighlight previously highlighted item
					this._selectedIndex = -1;
					for (var i = 0; i < children.length; i++) {
						var child = children[i];
						if (child._highlighted) {
							child._highlighted = undefined;
							$(child).removeClass("sel");
						}
						if (item == child) {
							this._selectedIndex = i;
							item._highlighted = true;
							$(item).addClass("sel");
						}
					}
				},

				validate: function() {
					var value = this._getValue();
					if (value == null) {
						if (this.options.isNullable) return;
						this.element.removeClass("cgd").addClass("ivd");
						return this.options.header + " is required.";
					}
				},

				getFilter: function() { }
			}
		}
	});


})(jQuery);