/*
 * GxUI Library 2.0.1
 * Copyright (c) 2009, Artech
 * All rights reserved.
 * 
 * GxUI Library is freely distributable under the terms of the BSD license.
 * 
 */

// Fallback mechanism, when Ext is not included

if (!window.Ext) {
	alert("ExtJS not found. To solve the problem, please follow the installation instructions: http://wiki.gxtechnical.com/commwiki/servlet/hwikibypageid?19312.");
}
/// <reference path="..\VStudio\vswd-ext_2.2.js" />
// gxui namespace and user controls base class definition


gxui = function () {
	var m_GenexusBuild = null;

	var fixCssReset = function () {
		var cssLines = [],
			cellSpacing,
			cellPadding;

		cssLines.push("table, table[cellspacing='0'] {",
					"    border-collapse: separate;",
					"}");

		// CSS rules are created on the fly so cellpadding and cellspacing values are not reseted.
		for (var i = 1; i <= 100; i++) {
			if (gxui.fixSpacingReset) {
				cellSpacing = [
					"table[cellspacing='", i, "'] {",
					"    border-collapse: separate;",
					"    border-spacing: ", i, "px;",
					"}"
				];

				cssLines.push(cellSpacing.join(""));
			}
			if (gxui.fixPaddingReset) {
				cellPadding = [
					"table[cellpadding='", i, "'] > tbody > tr > td:not([class]), table[cellpadding='", i, "'] > tbody > tr > th:not([class]) {",
					"    padding: ", i, "px;",
					"}"
				];

				cssLines.push(cellPadding.join(""));
			}
		}

		var head = document.getElementsByTagName('head')[0],
			styleEl = document.createElement('style');

		styleEl.type = 'text/css';

		if (styleEl.styleSheet) {
			styleEl.styleSheet.cssText = cssLines.join("");
		}
		else {
			styleEl.appendChild(document.createTextNode(cssLines.join("")));
		}
		head.appendChild(styleEl);
	};

	return {
		
		fixPaddingReset: true,

		
		fixSpacingReset: true,

		initialize: function () {

			// Initialize QuickTips
			Ext.tip.QuickTipManager.init();

			// Define a namespace for extensions of Ext components
			Ext.namespace('gxui.ext');
			// Define a namespace for object properties management classes
			Ext.namespace('gxui.Properties');
			// Define a namespace for GX interop classes
			Ext.namespace('gxui.GX');
			// Define a namespace for GxUI user extensions
			Ext.namespace('gxui.ux');

			gx.fx.obs.addObserver('gx.onready', this, function () {
				if (gx && Ext.ieVersion > 0 && Ext.ieVersion < 8) {
					if (gx.staticDirectory != "")
						this.StaticContent = gx.staticDirectory;
					else
						this.StaticContent = this.getCookie('StaticContent');

					Ext.BLANK_IMAGE_URL = gx.util.resourceUrl(gx.basePath + this.StaticContent + "Shared/ext/resources/themes/images/default/tree/s.gif", true);
				}

				// Fix CSS reset made by ExtJS that affects tables
				gxui.afterShow(fixCssReset, gxui);

				// Force popup size recalculation after showing controls
				if (gx.popup.ispopup()) {
					gxui.afterShow(function () {
						var popup = gx.popup.getPopup().window.gx.popup;
						Ext.defer(popup.autofit, 100, popup)
					}, gx.popup);
				}
			});

			// Default State provider
			Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
				expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365)) //365 days
			}));

			// For versions prior to build 55424, gx.lang.inherits function is overriden to allow
			// GxUI to work properly with older versions of GeneXus.
			var gxBuild = gxui.getGeneXusBuild();
			if (gxBuild) {
				if (gxBuild > 54798) {
					var docEl = Ext.fly(document.documentElement);
					if (docEl) {
						docEl.addCls("gxui-xev2");
						if (gxBuild <= 64355) {
							docEl.addCls("gxui-msg-fix");
						}
					}
				}
				if (gxBuild < 55424) {
					gx.lang.inherits = function (subclass, superclass) {
						var oldProt = subclass.prototype;
						subclass.prototype = new superclass();
						for (var pName in oldProt) {
							if (typeof (subclass.prototype[pName]) == 'undefined')
								subclass.prototype[pName] = oldProt[pName];
						}
						if (typeof (subclass.prototype.base) == 'undefined')
							subclass.prototype.base = superclass;

						subclass.prototype.constructor = function () {
							superclass.prototype.constructor.apply(this, arguments);
							oldProt.constructor.apply(this, arguments);
						};
					};
				}
			}
			
			// Override Ext.getBody() to allow ExtJS to work with SPA
			Ext.getBody = (function() {
				var body, domBody;
				return function() {
					if (!domBody || domBody != document.body) {
						domBody = document.body;
						body = Ext.get(domBody);
						return body;
					}
					return body;
				};
			}())
		},

		
		CBoolean: function (str) {
			if (str) {
				if (typeof (str) == 'string')
					return (str.toLowerCase() == "true")
				return str;
			}
			else
				return false;
		},


		
		clone: function (obj, fn) {
			if (obj instanceof Array)
				return gxui.copyArray(obj, fn);
			if (typeof (obj) != 'object')
				return obj;
			if (obj == null)
				return obj;
			if (obj.clone)
				return obj.clone();
			var cloneObj = new Object();
			for (var i in obj)
				cloneObj[i] = gxui.clone(obj[i], fn);
			if (fn && typeof (fn) == 'function')
				fn(cloneObj);
			return cloneObj;
		},

		
		copyArray: function (arr, fn) {
			var res = [];
			for (var i = 0; i < arr.length; i++)
				res.push(gxui.clone(arr[i], fn));
			return res;
		},

		getCookie: function (c_name) {
			if (document.cookie.length > 0) {
				c_start = document.cookie.indexOf(c_name + "=");
				if (c_start != -1) {
					c_start = c_start + c_name.length + 1;
					c_end = document.cookie.indexOf(";", c_start);
					if (c_end == -1) c_end = document.cookie.length;
					return unescape(document.cookie.substring(c_start, c_end));
				}
			}
			return "";
		},

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

		dateFormat: function () {
			var gxDF = gx.dateFormat;
			switch (gxDF) {
				case "MDY": return "m/d/y";
				case "MDY4": return "m/d/Y";
				case "YMD": return "y/m/d";
				case "Y4MD": return "Y/m/d";
				case "DMY": return "d/m/y";
				case "DMY4": return "d/m/Y";
			}
		},

		date: function (string) {
			return new Date.parseDate(string, this.dateFormat());
		},

		
		afterShow: function (fn, scope, options) {
			gxui.UserControlManager.afterShow(fn, scope, options);
		},

		
		tryPropertyMapping: function (targetObj, source, propertyMap) {
			for (var targetProp in propertyMap) {
				var sourceValue,
					sourceProp = propertyMap[targetProp],
					ignoreEmpty = false;

				if (typeof (propertyMap[targetProp]) == "object") {
					ignoreEmpty = propertyMap[targetProp].ignoreEmpty || false;
					sourceProp = propertyMap[targetProp].property;
				}

				if (typeof (source) == "function")
					sourceValue = source(sourceProp);
				else
					sourceValue = source[sourceProp];

				if (ignoreEmpty) {
					if (sourceValue)
						targetObj[targetProp] = sourceValue;
				}
				else {
					if (sourceValue !== undefined)
						targetObj[targetProp] = sourceValue;
				}
			}
		},

		
		getGeneXusBuild: function () {
			if (m_GenexusBuild === null) {
				try {
					var metaElements = document.getElementsByTagName('meta'),
						generatorEl = null,
						versionEl = null;
					for (var i = 0, len = metaElements.length; i < len; i++) {
						if (metaElements[i].name.toLowerCase() == "version")
							versionEl = metaElements[i];
						if (metaElements[i].name.toLowerCase() == "generator")
							generatorEl = metaElements[i];
					}

					var value = versionEl ? versionEl.getAttribute('content') : generatorEl.getAttribute('content');
					var matches = value.match(/-(\d+)$/);
					if (matches.length > 1)
						m_GenexusBuild = parseInt(matches[1]);
				}
				catch (e) {
					m_GenexusBuild = 0;
				}
			}

			return m_GenexusBuild;
		}
	};
} ();

gxui.initialize();
/// <reference path="..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.UserControl', {
	mixins: {
		observable: 'Ext.util.Observable'
	},

	
	unmanagedLayout: false,

	
	constructor: function (options) {
		this.setOptions(options)
		this.initialize();

		return this;
	},

	//private
	setOptions: function (options) {
		this.options = {
			register: true
		};

		for (property in (options || {})) {
			this.options[property] = options[property];
		}
	},

	//private
	initialize: function () {
		this.rendered = false;

		this.mixins.observable.constructor.call(this);

		this.addEvents({
			
			"show": true,
			
			"destroy": true
		});

		if (this.options.register)
			this.register();

		if (this.methods)
			this.addDeferredMethods(this.methods);
	},

	
	show: function () {
		if (!this.rendered) {
			Ext.onReady(function () {
				try {
					this.renderControl();
				}
				catch (e) {
					gx.dbg.logEx(e, 'gxui.js', 'show');
				}
				finally {
					this.fireEvent("show", this);
				}
			}, this);
		}
		else {
			try {
				if (this.onRefresh)
					this.onRefresh();
			}
			catch (e) {
				gx.dbg.logEx(e, 'gxui.js', 'show');
			}
			finally {
				this.fireEvent("show", this);
			}
		}
	},

	renderControl: function () {
		this.onRender();
		this.rendered = true;
		this.addToContainer();
		if (this.onAfterRender) {
			var control = this.getUnderlyingControl();
			if (control) {
				if (control.rendered)
					this.onAfterRender.call(this, control);
				else
					control.on('afterrender', this.onAfterRender, this, [control]);
			}
		}
	},
	
	
	forceRendering: function () {
		this.rendered = false;
	},

	
	destroy: function () {
		try {
			this.onDestroy();
		}
		catch (e) {
			gx.dbg.logEx(e, 'gxui.js', 'destroy');
		}

		this.fireEvent("destroy", this);
	},

	
	onRender: Ext.emptyFn,

	
	onRefresh: Ext.emptyFn,

	
	onDestroy: function () {
		var c = this.getUnderlyingControl();
		if (c) {
			var ct = c.ownerCt;
			if (ct) {
				if (ct.remove) {
					ct.remove(c);
				}
			}
			else {
				if (c.destroy) {
					c.destroy();
				}
			}
		}
	},

	
	getUnderlyingControl: function () {
		return false
	},

	
	addToParent: function () {
		return false;
	},

	
	register: function () {
		gxui.UserControlManager.register(this);
	},

	
	unregister: function () {
		gxui.UserControlManager.unregister(this);
	},

	
	registerCt: function (el, addFn, doLayoutFn, scope) {
		gxui.UserControlManager.registerContainer(this, el, addFn, doLayoutFn, scope);
	},

	
	unregisterCt: function (toRem) {
		gxui.UserControlManager.unregisterContainer(toRem);
	},

	
	addToContainer: function () {
		var control = this.getUnderlyingControl();
		if (control) {
			if (this.addToParent()) {
				gxui.UserControlManager.addToParentContainer(this, control);
			}
			else {
				if (!this.unmanagedLayout && !control.rendered) {
					control.render(this.getContainerControl());
				}
			}
		}
	},

	checkIfInline: function (el) {
		if (el.id.indexOf("gxHTMLWrp") >= 0 || el.hasCls("gx_usercontrol") || el.hasCls("gxui-uc-container"))
			el.setStyle("display", "inline");

		if (this.getContainerControl() == el.dom && gxui.CBoolean(this.AutoWidth) && this.getUnderlyingControl() && !this.getUnderlyingControl().ownerCt)
			el.setStyle("display", "inline-block");
	},

	
	getUniqueId: function () {
		var pO = this.ParentObject;
		return "gxui20" + (pO ? (pO.CmpContext ? "-" + pO.CmpContext : "") + "-" + pO.ServerClass.replace(/\./g, "-") || "" : "") + "-" + this.ControlName + (this.GridRow || "");
	},

	addDeferredMethods: function (methods) {
		for (var m in methods) {
			if (typeof (methods[m]) == 'function') {
				this[m] = Ext.bind(function () {
					var fn = arguments[arguments.length - 1],
						args = Array.prototype.slice.call(arguments, 0, arguments.length - 1);

					if (this.runDeferredMethod(m))
						return fn.apply(this, args);
					else
						gxui.afterShow(Ext.bind(fn, this, args), this, { single: true });
				}, this, [methods[m]], true);
			}
		}
	},

	runDeferredMethod: function (methodName) {
		var control = this.getUnderlyingControl();
		return control === false || (control && control.rendered);
	}
});


gxui.UserControlManager = function () {
	var mgr;
	var ucList = [];
	var ctList = [];

	var afterShowEvent;

	var initAfterShow = function () {
		afterShowEvent = new Ext.util.Event();
	};

	var allShownHandler = function () {
		afterShowEvent.fire();
		this.addControlsToContainer();
		for (var i = 0, len = ucList.length; i < len; i++) {
			var item = ucList[i],
				extUC = item.getUnderlyingControl();

			if (extUC) {
				if (!item.unmanagedLayout && !extUC.rendered)
					extUC.render(item.getContainerControl());
				else {
					// Fire doLayout function in those controls that don't have a parent control.
					if (extUC && !extUC.ownerCt && extUC.doLayout) {
						extUC.doLayout();
					}
					if (item.fixAutoDimensions) {
						item.fixAutoDimensions(extUC);
					}
				}
			}
			item.shown = false;
		}
	};
	
	var ucShowListener = function (uc) {
		try {
			var ucListItem = this.isRegisteredUC(uc)
			if (ucListItem) {
				ucListItem.shown = true;
			}

			var allShown = true;
			for (var i = 0, len = ucList.length; i < len; i++) {
				allShown = ucList[i].shown && allShown;
				if (!allShown)
					break;
			}

			if (allShown && afterShowEvent) {
				allShownHandler.call(this);
			}
		}
		catch (e) {
			gx.dbg.logEx(e, 'gxui.js', 'ucShowListener');
		}
	};

	gx.fx.obs.addObserver('gx.onobjectpostback', mgr, function () {
		for (var i = 0, len = ucList.length; i < len; i++) {
			if (ucList[i].shown === true) {
				allShownHandler.call(mgr);
				return;
			}
		}
	});
	
	mgr = {
		childControls: {},

		getUCList: function () {
			var l = [];
			for (var i = 0, len = ucList.length; i < len; i++) {
				l.push(ucList[i]);
			}
			return l;
		},

		getContainersList: function () {
			return ctList;
		},

		register: function (uc) {
			ucList.push(uc);
			uc.shown = false;

			uc.on("show", ucShowListener, this);

			uc.on("destroy", function (uc) {
				this.unregister(uc);
				this.unregisterContainer(uc);
				if (uc.afterShowHandler)
					afterShowEvent.removeListener(uc.afterShowHandler, uc);
			}, this);
		},

		unregister: function (uc) {
			var toRem = this.isRegisteredUC(uc);
			if (toRem) {
				for (var i = ucList.length - 1; i >= 0; i--) {
					if (toRem == ucList[i]) {
						ucList.splice(i, 1);
						break;
					}
				}
			}
		},

		registerContainer: function (uc, el, addFn, doLayoutFn, scope) {
			ctList.push({
				uc: uc,
				el: el,
				addFn: addFn,
				doLayoutFn: doLayoutFn,
				scope: scope
			});
		},

		unregisterContainer: function (obj) {
			toRem = this.isRegisteredContainer(obj);
			if (toRem) {
				for (var i = ctList.length - 1; i >= 0; i--) {
					if (toRem == ctList[i]) {
						ctList.splice(i, 1);
						break;
					}
				}
			}
		},

		isRegisteredUC: function (uc) {
			var obj = null;

			for (var i = 0, len = ucList.length; i < len; i++) {
				var item = ucList[i];
				if (uc == item) {
					obj = item;
					break;
				}
			}
			return obj;
		},

		isRegisteredContainer: function (el) {
			var ct = null;

			if (el.layout) {
				for (var i = 0, len = ctList.length; i < len; i++) {
					var item = ctList[i];
					if (el == item.scope) {
						ct = item;
						break;
					}
				}
			}
			else
				if (el.tagName) { // If el argument is a HTMLElement
					for (var i = 0, len = ctList.length; i < len; i++) {
						var item = ctList[i];
						if (el == item.el) {
							ct = item;
							break;
						}
					}
				}
				else { // If el argument is a gxui.UserControl
					uc = el;
					for (var i = 0, len = ctList.length; i < len; i++) {
						var item = ctList[i];
						if (uc == item.uc) {
							ct = item;
							break;
						}
					}
				}

			return ct;
		},

		setControlContainer: function (control, container) {
			if (!this.childControls)
				this.childControls = {};

			var containerId = container == 'ROOT' ? container : container.scope.id;
			if (!this.childControls[containerId])
				this.childControls[containerId] = [];
			this.childControls[containerId].push(control);
		},

		addToParentContainer: function (uc, control) {
			control.on('added', function () {
				control.width = undefined;
				control.height = undefined;
			}, uc);

			var findParentContainerFn = this.findParentContainer.closure(this, arguments);
			var afterShowEvent = gxui.afterShow(findParentContainerFn, this);
			
			uc.on('destroy', function () {
				if (afterShowEvent) {
					afterShowEvent.removeListener(findParentContainerFn, this);
				}
			}, this);
		},


		findParentContainer: function (uc, control) {
			try {
				var el = Ext.get(uc.getContainerControl());
				uc.checkIfInline(el);
				for (var el = Ext.get(uc.getContainerControl()); el; el = el.parent("div")) {
					var container = gxui.UserControlManager.isRegisteredContainer(el.dom)
					uc.checkIfInline(el);
					if (container) {
						this.setControlContainer(control, container);
						return;
					}
				}

				// Controls that don't have a parent container
				this.setControlContainer(control, 'ROOT');
			}
			catch (e) {
				gx.dbg.logEx(e, 'gxui.UserControl.js', 'addToParentContainer->' + uc.getUniqueId());
			}
		},

		addControlsToContainer: function () {
			try {
				var containers = this.getContainersList();
				for (var i = 0, len = containers.length; i < len; i++) {
					var container = containers[i],
						children = this.childControls[container.scope.id];
					var safeChildren = [];
					if (children) {
						for (var j = 0, len2 = children.length; j < len2; j++) {
							if (!children[j].isDestroyed)
								safeChildren.push(children[j]);
						}
						if (safeChildren && safeChildren.length > 0)
							container.addFn.call(container.scope, safeChildren);
					}
				}

				delete this.childControls;
			}
			catch (e) {
				gx.dbg.logEx(e, 'gxui.UserControl.js', 'addControlsToContainer');
			}
		},

		
		afterShow: function (fn, scope, options) {
			if (!afterShowEvent)
				initAfterShow();

			scope.afterShowHandler = fn;
			afterShowEvent.addListener(fn, scope, options);
			return afterShowEvent;
		}
	};

	return mgr;
} ();

Ext.define('gxui.container.NestedViewport', {
	extend: 'Ext.container.Container',

	initComponent: function () {
		this.callParent(arguments);

		var html = Ext.fly(document.body.parentNode);
		html.addCls(Ext.baseCSSPrefix + 'viewport');
		if (this.autoScroll) {
			html.setStyle('overflow', 'auto');
		}

		this.renderTo = Ext.get(this.renderTo);
		this.renderTo.addCls("x-nested");

		this.el = this.renderTo;
		this.el.setHeight = Ext.emptyFn;
		this.el.setWidth = Ext.emptyFn;
		this.el.setSize = Ext.emptyFn;
		this.el.dom.scroll = 'no';
		this.allowDomMove = false;
		Ext.EventManager.onWindowResize(this.fireResize, this);
		this.width = Ext.Element.getViewportWidth();
		this.height = Ext.Element.getViewportHeight();
	},

	fireResize: function (w, h) {
		this.setSize(w, h);
	}
});


/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.Panel', {
	extend: 'gxui.UserControl',

	

	//private
	SetToolbarData: function (data) {
		this.ToolbarData = data;
	},

	//private
	GetToolbarData: function (data) {
		return this.ToolbarData;
	},

	onRender: function () {
		var config = this.getConfig();

		if (gxui.CBoolean(this.ShowAsWindow)) {
			config.closeAction = "hide";
			config.renderTo = 'MAINFORM';
			config.modal = gxui.CBoolean(this.Modal);
			config.constrainHeader = true;
			this.m_panel = new Ext.create('Ext.window.Window', config);

			if (gx.lang.gxBoolean(this.Visible)) {
				this.m_panel.show();
			}
		}
		else {
			this.m_panel = Ext.create('Ext.panel.Panel', config);
		}

		// Register as UC Container
		this.registerCt(this.getChildContainer("Body"), this.m_panel.add, this.m_panel.doLayout, this.m_panel);
	},

	onRefresh: function () {
		var panel = this.m_panel;
		if (this.Title != panel.title) {
			panel.setTitle(this.Title);
		}
		if (!panel.ownerCt) {
			var width = parseInt(this.Width),
				height = parseInt(this.Height);

			if ((!gxui.CBoolean(this.AutoWidth) && panel.getWidth() != width) || (!gxui.CBoolean(this.AutoHeight) && panel.getHeight() != height)) {
				panel.animate({
					to: {
						width: width,
						height: height
					}
				});
			}
		}

		if (gx.lang.gxBoolean(this.Visible) && !this.m_panel.isVisible()) {
			panel.show();
		}
		else {
			if (!gx.lang.gxBoolean(this.Visible) && panel.isVisible()) {
				panel.hide();
			}
		}

		if (gxui.CBoolean(this.UseToolbar)) {
			this.m_gxTbar.SetData(this.ToolbarData);
			this.m_gxTbar.onRefresh();
		}
	},

	getUnderlyingControl: function () {
		return this.m_panel;
	},

	addToParent: function () {
		return !gxui.CBoolean(this.ShowAsWindow) && (this.AddToParentGxUIControl == undefined || gxui.CBoolean(this.AddToParentGxUIControl));
	},

	//private
	getConfig: function () {
		var dockedItems = [],
			bodyEl = Ext.get(this.getChildContainer("Body"));

		if (gxui.CBoolean(this.UseToolbar)) {
			var position = this.ToolbarPosition || 'top';

			this.m_gxTbar = Ext.create('gxui.Toolbar', { register: false });
			this.m_toolbar = this.m_gxTbar.createToolbar({
				id: this.getUniqueId() + "_Toolbar",
				data: this.ToolbarData,
				vertical: !(position == 'bottom' || position == 'top'),
				dock: position,
				container: this
			});

			dockedItems.push(this.m_toolbar);
		}

		bodyEl.enableDisplayMode().show();
		if (gxui.CBoolean(this.AutoHeight)) {
			bodyEl.setStyle({
				height: 'auto',
				display: 'inline-block'
			});
		}

		var config = {
			contentEl: bodyEl,
			id: this.getUniqueId(),
			autoWidth: gxui.CBoolean(this.AutoWidth),
			autoHeight: gxui.CBoolean(this.AutoHeight),
			autoScroll: (this.Layout == 'default'),
			frame: gxui.CBoolean(this.Frame),
			border: gxui.CBoolean(this.Border) ? 2 : false,
			collapsible: gxui.CBoolean(this.Collapsible),
			collapsed: gxui.CBoolean(this.Collapsed),
			animCollapse: gxui.CBoolean(this.AnimateCollapse),
			resizable: gxui.CBoolean(this.Resizable),
			dockedItems: dockedItems,
			listeners: this.getListeners(),
			stateful: gxui.CBoolean(this.Stateful),
			stateId: (this.StateId != "") ? this.StateId : undefined,
			layout: this.Layout == 'default' ? undefined : this.Layout,
			bodyCls: "gxui-noreset"
		};

		gxui.tryPropertyMapping(config, this, {
			"title": "Title",
			"headerPosition": "HeaderPosition",
			"minWidth": "MinWidth",
			"minHeight": "MinHeight",
			"maxWidth": "MaxWidth",
			"maxHeight": "MaxHeight",
			"collapseDirection": "CollapseDirection",
			"resizeHandles": "Handles",
			"iconCls": "IconCls",
			"cls": "Cls"
		});

		if (!gxui.CBoolean(this.AutoWidth))
			config.width = parseInt(this.Width);

		if (!gxui.CBoolean(this.AutoHeight))
			config.height = parseInt(this.Height);

		if (!gx.lang.gxBoolean(this.Visible))
			config.hidden = true;

		return config;
	},

	fixAutoDimensions: function (panel) {
		if (!this.fixingAutoDims) {
			this.fixingAutoDims = true;
			if (panel.rendered) {
				if (gxui.CBoolean(this.AutoHeight)) {
					panel.el.setHeight('auto');
					panel.body.setHeight('auto');
					if (panel.header && (panel.headerPosition == "top" || panel.headerPosition == "bottom")) {
						panel.body.setStyle('margin-bottom', Ext.dom.AbstractElement.addUnits(panel.header.getHeight(), "px"));
					}
				}

				if (gxui.CBoolean(this.AutoWidth)) {
					panel.el.setWidth('auto');
					panel.body.setWidth('auto');
					if (panel.header && (panel.headerPosition == "top" || panel.headerPosition == "bottom")) {
						Ext.defer(panel.header.setWidth, 50, panel.header, ['auto']);
					}
				}
			}
			this.fixingAutoDims = false;
		}
	},

	//private
	getListeners: function () {
		return {
			'collapse': function () {
				this.Collapsed = "true";
			},

			'expand': function () {
				this.Collapsed = "false";
			},

			'hide': function () {
				this.Visible = false;
				
				if (this.OnClose) {
					this.OnClose();
				}
			},

			'add': this.fixAutoDimensions,

			'afterrender': this.fixAutoDimensions,

			scope: this
		};
	},

	methods: {
		// Methods
		
		ChangeToolbar: function (toolbarData) {
			if (this.m_gxTbar)
				this.m_toolbar = this.m_gxTbar.ChangeToolbar(toolbarData, this.getUniqueId() + "_Toolbar", this);
		},

		
		Collapse: function (animate) {
			this.m_panel.collapse(this.CollapseDirection, animate);
		},

		
		Expand: function (animate) {
			this.m_panel.expand(animate);
		},

		
		DisableToolbarItem: function (itemId) {
			this.m_gxTbar.DisableItem(itemId);
		},

		
		EnableToolbarItem: function (itemId) {
			this.m_gxTbar.EnableItem(itemId);
		},

		
		HideToolbarItem: function (itemId) {
			this.m_gxTbar.HideItem(itemId);
		},

		
		ShowToolbarItem: function (itemId) {
			this.m_gxTbar.ShowItem(itemId);
		},

		
		CenterWindow: function () {
			if (gxui.CBoolean(this.ShowAsWindow)) {
				this.m_panel.center();
			}
		}
	}
});
/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.Toolbar', {
	extend: 'gxui.UserControl',

	initialize: function () {
		this.callParent();

		this.ButtonPressedId = "";
		this.EditFieldValue = "";

		this.addEvents({
			
			"beforebuttonpressed": true,
			
			"buttonpressed": true,
			
			"editfieldkeypress": true,
			
			"editfieldblur": true
		});


		// Register default Toolbar item resolvers
		gxui.Toolbar.ItemResolvers.register({
			"Button": function (toolbar, button) {
				var config = {
					id: toolbar.getUniqueButtonId(button.Id),
					gxConfirmation: gxui.CBoolean(button.AskConfirmation) ? button.Confirmation : false,
					cls: toolbar.getBtnCls(button),
					enableToggle: gxui.CBoolean(button.EnableToggle),
					pressed: gxui.CBoolean(button.Pressed),
					disabled: gxui.CBoolean(button.Disabled),
					hidden: gxui.CBoolean(button.Hidden),
					handler: toolbar.buttonClickHandler,
					isDropTarget: gxui.CBoolean(button.IsDropTarget),
					scope: toolbar,
					RefreshData: gxui.CBoolean(button.RefreshData)
				};

				gxui.tryPropertyMapping(config, button, {
					"gxid": "Id",
					"text": "Text",
					"tooltip": "Tooltip",
					"icon": "Icon",
					"iconCls": "IconCls",
					"rowspan": "RowSpan",
					"colspan": "ColSpan",
					"iconAlign": { property: "IconAlign", ignoreEmpty: true },
					"arrowAlign": { property: "ArrowAlign", ignoreEmpty: true },
					"scale": { property: "Scale", ignoreEmpty: true },
					"width": { property: "Width", ignoreEmpty: true }
				});

				return config;
			},

			"Text": function (toolbar, button) {
				return button.Text;
			},

			"Edit": function (toolbar, button) {
				var edit = Ext.create('Ext.form.field.Text', {
					id: toolbar.getUniqueButtonId(button.Id),
					cls: button.Cls,
					width: button.Width || 180,
					disabled: gxui.CBoolean(button.Disabled),
					hidden: gxui.CBoolean(button.Hidden),
					enableKeyEvents: true,
					value: button.Value || undefined
				});

				if (edit.Text != '')
					edit.emptyText = button.Text;

				edit.on({
					"keypress": {
						fn: function (field, e) {
							this.fireEvent("editfieldkeypress", this, field, e);
							if (e.getKey() == Ext.EventObject.ENTER) {
								e.stopEvent();
								this.editActionHandler(field);
							}
						},
						scope: toolbar
					},
					"blur": {
						fn: function (field) {
							this.fireEvent("editfieldblur", this, field);
							this.editActionHandler(field);
						},
						scope: toolbar
					}
				});
				edit.gxid = button.Id;

				return edit;
			},

			"Fill": function () {
				return Ext.create('Ext.toolbar.Fill');
			},

			"Separator": function () {
				return "-";
			},

			"Menu": function (toolbar, button) {
				var menuItems = [];

				if (button.Items) {
					Ext.each(button.Items, function (item, index, allItems) {
						menuItems.push(toolbar.getConfig(item));
					});
				}

				var config = {
					hidden: gxui.CBoolean(button.Hidden),
					menu: {
						items: menuItems,
						ignoreParentClicks: true
					},
					cls: toolbar.getBtnCls(button),
					disabled: gxui.CBoolean(button.Disabled)
				};

				gxui.tryPropertyMapping(config, button, {
					"text": "Text",
					"tooltip": "Tooltip",
					"icon": "Icon",
					"iconCls": "IconCls",
					"rowspan": "RowSpan",
					"colspan": "ColSpan",
					"iconAlign": { property: "IconAlign", ignoreEmpty: true },
					"arrowAlign": { property: "ArrowAlign", ignoreEmpty: true },
					"scale": { property: "Scale", ignoreEmpty: true },
					"width": { property: "Width", ignoreEmpty: true }
				});

				return config;
			},

			"SplitButton": function (toolbar, button) {
				var splitButton = gxui.Toolbar.ItemResolvers.get(gxui.Toolbar.ItemType.Menu)(toolbar, button);

				splitButton.gxid = button.Id;
				splitButton.gxConfirmation = gxui.CBoolean(button.AskConfirmation) ? button.Confirmation : false;
				splitButton.xtype = 'splitbutton';
				splitButton.enableToggle = gxui.CBoolean(button.EnableToggle);
				splitButton.pressed = gxui.CBoolean(button.Pressed);
				if (gxui.CBoolean(button.EnableToggle)) {
					splitButton.toggleHandler = toolbar.buttonClickHandler;
				}
				else {
					splitButton.handler = toolbar.buttonClickHandler;
				}
				splitButton.scope = toolbar;

				return splitButton;
			},

			"Group": function (toolbar, button) {
				var groupItems = [];

				if (button.Items) {
					Ext.each(button.Items, function (item, index, allItems) {
						groupItems.push(toolbar.getConfig(item));
					});
				}

				var config = {
					xtype: 'buttongroup',
					defaults: {},
					items: groupItems
				};

				gxui.tryPropertyMapping(config, button, {
					"title": "Text",
					"columns": "GroupColumns"
				});

				gxui.tryPropertyMapping(config.defaults, button, {
					"iconAlign": { property: "IconAlign", ignoreEmpty: true },
					"arrowAlign": { property: "ArrowAlign", ignoreEmpty: true },
					"scale": { property: "Scale", ignoreEmpty: true }
				});

				return config;
			},

			"ComboBox": function (toolbar, comboBox) {
				var config = {
					xtype: 'combobox',
					gxid: comboBox.Id,
					cls: toolbar.getBtnCls(comboBox),
					disabled: gxui.CBoolean(comboBox.Disabled),
					hidden: gxui.CBoolean(comboBox.Hidden),
					editable: true,
					triggerAction: 'all',
					selectOnFocus: true,
					disableKeyFilter: false,
					forceSelection: true,
					queryMode: 'local',
					store: {
						autoDestroy: true,
						fields: ['id', 'dsc'],
						data: []
					},
					displayField: 'dsc',
					valueField: 'id',
					listeners: {
						'select': function (field) {
							this.editActionHandler(field);
						},
						scope: toolbar
					}
				};

				if (comboBox.Items) {
					for (var i = 0, len = comboBox.Items.length; i < len; i++) {
						config.store.data.push({
							id: comboBox.Items[i].Id,
							dsc: comboBox.Items[i].Text
						});
					}
				}

				gxui.tryPropertyMapping(config, comboBox, {
					"gxid": "Id",
					"text": "Text",
					"tooltip": "Tooltip",
					"icon": "Icon",
					"iconCls": "IconCls",
					"rowspan": "RowSpan",
					"colspan": "ColSpan",
					"iconAlign": { property: "IconAlign", ignoreEmpty: true },
					"arrowAlign": { property: "ArrowAlign", ignoreEmpty: true },
					"scale": { property: "Scale", ignoreEmpty: true },
					"width": { property: "Width", ignoreEmpty: true },
					"value": { property: "Value", ignoreEmpty: true },
					"emptyText": { property: "Text", ignoreEmpty: true }
				});

				return config;
			}
		});
	},

	//private
	SetData: function (data) {
		this.Data = data;
	},

	//private
	GetData: function (data) {
		return this.Data;
	},

	//private
	GetToolbar: function (add) {
		return this.m_toolbar;
	},

	onRender: function () {
		this.createToolbar().render(this.getContainerControl());
	},

	onRefresh: function () {
		this.refreshButtons(this.Data.Buttons, this.m_toolbar.items);
	},

	getUnderlyingControl: function () {
		return this.m_toolbar;
	},

	//private
	createToolbar: function (options) {
		var vertical = false,
			itemId;
		if (options) {
			if (options.id) {
				this.getUniqueId = function () {
					return options.id;
				};
			}

			if (options.data) {
				this.SetData(options.data);
			}

			if (options.container) {
				options.container.buttonActionHandler = this.buttonActionHandler;
				options.container.editActionHandler = this.editActionHandler;

				this.buttonActionHandler = Ext.bind(this.buttonActionHandler, options.container);
				this.editActionHandler = Ext.bind(this.editActionHandler, options.container);
			}

			if (options.on) {
				this.on(options.on);
			}

			if (options.vertical)
				vertical = options.vertical;

			itemId = options.itemId;
		}

		var config = {
			id: this.getUniqueId(),
			stateful: false,
			items: this.createButtons(),
			listeners: {},
			vertical: vertical,
			itemId: itemId
		};

		if (options && options.container) {
			config.dock = options.dock || 'top';
		}

		if (!options || !options.container) {
			if (this.Width != 'auto') {
				config.width = parseInt(this.Width, 10);
			}

			if (this.Height != 'auto') {
				config.height = parseInt(this.Height, 10);
			}
		}

		config.listeners['afterrender'] = {
			fn: this.adjustWidth,
			delay: 300
		};
		config.listeners.scope = this;

		this.m_toolbar = Ext.create('Ext.toolbar.Toolbar', config);

		return this.m_toolbar;
	},

	//private
	createButtons: function () {
		var toolbarItems = [];
		if (this.Data && this.Data.Buttons) {
			Ext.each(this.Data.Buttons, function (item, index, allItems) {
				if (!item.Type) {
					item.Type = gxui.Toolbar.ItemType.Button;
				}
				toolbarItems.push(this.getConfig(item));
				if (this.SeparateAll && allItems[index + 1])
					toolbarItems.push('-');
			}, this);
		}
		return toolbarItems;
	},

	//private
	buttonClickHandler: function (btn, e) {
		if (this.fireEvent("beforebuttonpressed", this, btn, e) !== false) {
			if (btn.gxConfirmation) {
				var processResult = function (option, text) {
					if (option == 'ok')
						this.buttonActionHandler(btn, e);
				};

				var msgBox = new Ext.window.MessageBox({
					buttonText: {
						ok: btn.gxConfirmation.OKButtonText,
						cancel: btn.gxConfirmation.CancelButtonText
					},
					listeners: {
						'afterrender': function (mb) {
							// Put focus on Cancel button by default.
							mb.defaultFocus = 3;
						}
					}
				});

				msgBox.show({
					title: btn.gxConfirmation.Title,
					msg: btn.gxConfirmation.Message,
					buttons: Ext.Msg.OKCANCEL,
					fn: processResult,
					scope: this,
					animateTarget: btn.getEl(),
					icon: Ext.Msg.QUESTION
				});
			}
			else {
				this.buttonActionHandler(btn, e);
			}
			this.fireEvent("buttonpressed", this, btn, e);
		}
	},

	//private
	buttonActionHandler: function (btn, e) {
		
		if (this.ButtonPressed) {
			this.ButtonPressedId = btn.gxid;
			this.ButtonPressed();
		}
	},

	//private
	editActionHandler: function (field) {
		this.EditFieldValue = field.getValue();
		this.buttonActionHandler(field);
	},

	//private
	getConfig: function (button) {
		if (button.id && button.id.indexOf("ext") == 0)
			return button;

		if (!button.Type)
			button.Type = gxui.Toolbar.ItemType.Button;

		var resolver = gxui.Toolbar.ItemResolvers.get(button.Type) || gxui.Toolbar.ItemResolvers.get(gxui.Toolbar.ItemType.Button);
		if (resolver)
			return resolver(this, button);
	},

	//private
	defineBtnsDropTarget: function () {
		this.m_toolbar.items.each(function (item, pos) {
			if (item.type == "button" && item.isDropTarget) {
				var dt = new Ext.dd.DropTarget(item.getEl(), { ddGroup: 'GridDD' });
				dt._btn = item;
				dt._scope = this;
				dt.notifyOver = function (source, e, data) {
					if (data.grid) {
						return 'x-dd-drop-ok';
					}
					return 'x-dd-drop-nodrop';
				};
				dt.notifyDrop = function (source, e, data) {
					if (data.grid) {
						this._scope.buttonActionHandler(this._btn);
						return true;
					}
					return false;
				};
				dt.notifyEnter = function (source, e, data) {
					if (data.grid) {
						return 'x-dd-drop-ok';
					}
					return 'x-dd-drop-nodrop';
				};
			}
		},
		this);
	},

	//private
	refreshButtons: function (buttons, renderedButtons) {
		var i = 0;
		var ItemType = gxui.Toolbar.ItemType;
		renderedButtons.each(function (renderedBtn) {
			var button = buttons[i];
			if (button) {
				if (!gxui.CBoolean(button.Disabled) && renderedBtn.disabled) {
					if (renderedBtn.enable)
						renderedBtn.enable();
				}
				else {
					if (gxui.CBoolean(button.Disabled) && !renderedBtn.disabled) {
						if (renderedBtn.disable)
							renderedBtn.disable();
					}
				}

				if (gxui.CBoolean(button.Hidden) && !renderedBtn.hidden) {
					if (renderedBtn.hide)
						renderedBtn.hide();
				}
				else {
					if (!gxui.CBoolean(button.Hidden) && renderedBtn.hidden) {
						if (renderedBtn.show)
							renderedBtn.show();
					}
				}

				if (button.Type == ItemType.Text)
					renderedBtn.setText(button.Text);

				if ((button.Type == ItemType.Menu || button.Type == ItemType.SplitButton) && button.Items && renderedBtn.menu) {
					this.refreshButtons(button.Items, renderedBtn.menu.items);
				}
			}
			i += 1;
		}, this)

		this.adjustWidth(this.m_toolbar);
	},

	//private
	getUniqueButtonId: function (btnId) {
		return this.getUniqueId() + "_btn_" + btnId;
	},

	//private
	findItem: function (id, items) {
		var ItemType = gxui.Toolbar.ItemType;
		var searchedItem;
		Ext.each(items, function (item) {
			if (item.Id == id) {
				searchedItem = item;
			}
			else {
				if ((item.Type == ItemType.Menu || item.type == ItemType.SplitButton) && item.Items) {
					searchedItem = this.findItem(id, item.Items);
				}
			}

			if (searchedItem) {
				return false;
			}
		}, this);
		return searchedItem;
	},

	//private
	changeItemPropertyValue: function (itemId, propertyId, propertyValue) {
		var item = this.findItem(itemId, this.Data.Buttons);
		if (item) {
			item[propertyId] = propertyValue;
		}
		return item;
	},

	//private
	getBtnCls: function (btn) {
		if (!btn.Cls) {
			if (btn.Icon) {
				return (btn.Text) ? "x-btn-text-icon" : "x-btn-icon";
			}
			else {
				return "x-btn-text";
			}
		}
		return btn.Cls;
	},

	adjustWidth: function (toolbar) {
		if (!toolbar.ownerCt) {
			if (this.Width == 'auto') {
				var lastItem = null;
				toolbar.items.each(function (item) {
					if (item.isVisible())
						lastItem = item;
				})

				if (lastItem) {
					toolbar.setWidth(100); // WA
					width = lastItem.el.getLeft(true) + lastItem.el.getWidth() + toolbar.el.getFrameWidth('l r');
					toolbar.setWidth(width);
				}
			}
		}
	},

	methods: {
		// Methods
		
		ChangeToolbar: function (toolbarData, id, container) {
			this.m_toolbar.removeAll();
			this.m_toolbar.add(this.createButtons());
			return this.m_toolbar;
		},

		
		DisableItem: function (itemId) {
			this.changeItemPropertyValue(itemId, "Disabled", true);
			this.refreshButtons(this.Data.Buttons, this.m_toolbar.items);
		},

		
		EnableItem: function (itemId) {
			this.changeItemPropertyValue(itemId, "Disabled", false);
			this.refreshButtons(this.Data.Buttons, this.m_toolbar.items);
		},

		
		HideItem: function (itemId) {
			this.changeItemPropertyValue(itemId, "Hidden", true);
			this.refreshButtons(this.Data.Buttons, this.m_toolbar.items);
		},

		
		ShowItem: function (itemId) {
			this.changeItemPropertyValue(itemId, "Hidden", false);
			this.refreshButtons(this.Data.Buttons, this.m_toolbar.items);
		}
	}
});


gxui.Toolbar.ItemType = {
	Button: "Button",
	Text: "Text",
	Edit: "Edit",
	Fill: "Fill",
	Separator: "Separator",
	Menu: "Menu",
	SplitButton: "SplitButton",
	Group: "Group"
};


gxui.Toolbar.ItemResolvers = {
	// private
	items: {},

	
	register: function (type, resolver) {
		if (typeof (type) == 'string')
			this.items[type] = resolver;
		else if (typeof (type) == 'object') {
			for (var item in type) {
				if (typeof (type[item]) == 'function') {
					this.items[item] = type[item];
				}
			}
		}
	},

	
	unregister: function (type) {
		delete this.items[type];
	},

	
	get: function (type) {
		return this.items[type];
	}
};
/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.Layout', function () {
	var regions = ["North", "West", "Center", "East", "South"];

	return {
		extend: 'gxui.UserControl',

		onRender: function () {
			var regionsCfg = Ext.Array.map(regions, function (regionName) {
				return this.createConfig(regionName);
			}, this);

			if (this.Width) {
				this.Width = parseInt(this.Width);
			}

			if (this.Height) {
				this.Height = parseInt(this.Height);
			}

			if (gxui.CBoolean(this.Nested)) {
				this.m_layout = Ext.create('Ext.panel.Panel', {
					id: this.getUniqueId(),
					border: 0,
					items: regionsCfg,
					layout: 'border',
					cls: this.Cls || undefined,
					width: this.Width != 0 ? this.Width : 'auto',
					height: this.Height != 0 ? this.Height : 'auto',
					stateful: gxui.CBoolean(this.Stateful),
					stateId: (this.StateId != "") ? this.StateId : undefined
				});
			}
			else {
				if (this.getContainerControl()) {
					this.m_layout = Ext.create('gxui.container.NestedViewport', {
						id: this.getUniqueId(),
						items: regionsCfg,
						layout: 'border',
						cls: this.Cls || undefined,
						renderTo: 'MAINFORM',
						stateful: gxui.CBoolean(this.Stateful),
						stateId: (this.StateId != "") ? this.StateId : undefined
					});
				}
			}

			// Register this User Control as a container. Each region of the layout is registered
			// as an individual container.
			this.registerAsContainer();
		},

		onRefresh: function () {
			Ext.each(regions, this.refreshRegion, this);
		},

		onAfterRender: function () {
			this.displayRegions();
			// WA to avoid problems with focus on first field if it's inside de Layout
			if (document.activeElement == document.body) {
				gx.fn.setFocusOnload();
			}
		},

		getUnderlyingControl: function () {
			return this.m_layout;
		},

		addToParent: function () {
			return gxui.CBoolean(this.Nested);
		},

		getMargins: function (regionKey, str) {
			var margins = this[regionKey + "Margins"];
			if (margins)
				return margins.replace(/,/ig, ' ');
		},

		getProperty: function (regionKey, propName) {
			var value = this[regionKey + propName];

			if (regionKey == "Center" && (propName == "Width" || propName == "MaxWidth" || propName == "MinWidth"))
				return undefined;

			if ((propName == "Width" || propName == "Height") && (typeof (value) == "string" && value.indexOf("%") == -1))
				return parseInt(value);

			if (value == "true" || value == "false")
				return gxui.CBoolean(value);
			else if (propName.indexOf("Margins") >= 0)
				return this.getMargins(regionKey, value);
			else if (propName.indexOf("Border") >= 0)
				return gx.lang.gxBoolean(value) ? undefined : 0;
			else if (propName == "Layout")
				return value == "default" ? undefined : value;
			else
				return value;
		},

		setRegionProperty: function (regionKey, propName, value) {
			if (regionKey) {
				var r = regionKey.toLowerCase();
				r = r.charAt(0).toUpperCase() + r.substr(1);
				if (this[r + propName]) {
					this[r + propName] = value;
				}
			}
		},

		createConfig: function (regionKey) {
			var config = {
				id: this.getUniqueId() + "-" + regionKey,
				itemId: regionKey.toLowerCase(),
				region: regionKey.toLowerCase(),
				contentEl: this.getChildContainer(regionKey),
				autoScroll: (this.getProperty(regionKey, "Layout") != "fit") ? this.getProperty(regionKey, "AutoScroll") : false,
				cls: "x-region-" + regionKey.toLowerCase(),
				bodyCls: "gxui-noreset",
				duration: this.getProperty(regionKey, "Duration") / 1000,
				listeners: {
					'collapse': function (p) {
						this.setRegionProperty(p.region, "Collapsed", "true");
					},

					'expand': function (p) {
						this.setRegionProperty(p.region, "Collapsed", "false");
					},

					scope: this
				},
				stateful: gxui.CBoolean(this.Stateful),
				stateId: (this.StateId != "") ? this.StateId + "-" + regionKey : undefined,
				stateEvents: gxui.CBoolean(this.Stateful) ? ['collapse', 'expand'] : undefined
			};

			if (!this.getProperty(regionKey, "TitleBar")) {
				config.header = false;
			}

			gxui.tryPropertyMapping(config, Ext.bind(this.getProperty, this, [regionKey], 0), {
				"hidden": "Hidden",
				"split": "Split",
				"title": "Title",
				"width": "Width",
				"height": "Height",
				"minWidth": "MinWidth",
				"minHeight": "MinHeight",
				"maxWidth": "MaxWidth",
				"maxHeight": "MaxHeight",
				"margins": "Margins",
				"collapsible": "Collapsible",
				"collapsed": "Collapsed",
				"floatable": "Floatable",
				"animate": "Animate",
				"animFloat": "Animate",
				"layout": "Layout",
				"border": "Border"
			});

			return config;
		},

		displayRegions: function () {
			var layout = this.m_layout;

			var displayRegion = function (region) {
				Ext.fly(layout.child('#' + region.toLowerCase()).contentEl).setDisplayed(true);
			};

			Ext.each(regions, displayRegion);
		},

		registerAsContainer: function () {
			Ext.each(regions, function (regionName) {
				var region = this.getRegion(regionName);
				this.registerCt(region.contentEl, region.add, region.doLayout, region);
			}, this);
		},

		getRegion: function (regionKey) {
			return this.m_layout.child('#' + regionKey.toLowerCase())
		},

		refreshRegion: function (regionKey) {
			var region = this.getRegion(regionKey);
			if (region) {
				region.setTitle(this.getProperty(regionKey, "Title"));
			}
		},

		methods: {
			// Methods
			
			CollapseRegion: function (regionKey, animate) {
				var region = this.getRegion(regionKey);

				if (region) {
					region.collapse(animate);
					this.setRegionProperty(regionKey, "Collapsed", "true");
				}
			},

			
			ExpandRegion: function (regionKey, animate) {
				var region = this.getRegion(regionKey);

				if (region) {
					region.expand(animate);
					this.setRegionProperty(regionKey, "Collapsed", "false");
				}
			},

			
			HideRegion: function (regionKey) {
				var region = this.getRegion(regionKey);

				if (region) {
					region.hide();
					this.m_layout.doLayout();
					this.setRegionProperty(regionKey, "Hidden", "true");
				}
			},

			
			ShowRegion: function (regionKey) {
				var region = this.getRegion(regionKey);

				if (region) {
					region.show();
					this.m_layout.doLayout();
					this.setRegionProperty(regionKey, "Hidden", "false");
				}
			}
		}
	};
} ());

/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.Treeview', {
	extend: 'gxui.UserControl',

	initialize: function () {
		this.callParent(arguments);

		this.NotifyContext = "false";
		this.NotifyDataType = 'gxuiTreeNode';
		this.CheckedNodes = [];
	},

	// Databinding
	SetChildren: function (data) {
		this.Children = data;
	},

	// Databinding
	GetChildren: function (data) {
		return this.Children;
	},

	// Databinding
	SetCheckedNodes: function (data) {
		this.CheckedNodes = data;
	},

	// Databinding
	GetCheckedNodes: function (data) {
		this.CheckedNodes = [];
		if (this.m_tree) {
			var checkedNodes = this.m_tree.getChecked();
			this.CheckedNodes = Ext.Array.map(checkedNodes, function (node) {
				return node.data.id;
			}, this);
		}
		return this.CheckedNodes
	},

	// Databinding
	SetUncheckedNodes: function (data) {
		this.UncheckedNodes = data;
	},

	// Databinding
	GetUncheckedNodes: function (data) {
		this.UncheckedNodes = [];
		if (this.m_tree) {
			var root = this.m_tree.getRootNode();
			if (root) {
				var nodes = [];
				root.cascadeBy(function () {
					if (this.data.checked === false) {
						nodes.push(this.data.id);
					}
				});
				this.UncheckedNodes = nodes;
			}
		}
		return this.UncheckedNodes;
	},

	// Databinding
	SetDropData: function (data) {
		this.DropData = data;
	},

	// Databinding
	GetDropData: function (data) {
		return this.DropData;
	},

	// Databinding
	SetSelectedNodeData: function (data) {
		this.SelectedNodeData = data;
	},

	// Databinding
	GetSelectedNodeData: function (data) {
		return this.SelectedNodeData;
	},

	// Databinding
	SetSelectedNodes: function (data) {
		this.SelectedNodes = data;
	},

	// Databinding
	GetSelectedNodes: function () {
		return this.SelectedNodes;
	},

	onRender: function () {
		var Tree = Ext.tree;
		var ddGroup = this.DragDropGroup || undefined;

		this.Width = parseInt(this.Width);
		this.Height = parseInt(this.Height);
		this.EnableCheckbox = gxui.CBoolean(this.EnableCheckbox);
		this.LazyLoading = gxui.CBoolean(this.LazyLoading);

		var store = this.createStore();

		var config = {
			id: this.getUniqueId(),
			title: this.Title,
			frame: gxui.CBoolean(this.Frame),
			border: gxui.CBoolean(this.Border) ? undefined : 0,
			cls: this.Cls,
			animate: gxui.CBoolean(this.Animate),
			rootVisible: gxui.CBoolean(this.RootVisible),
			lines: gxui.CBoolean(this.ShowLines),
			store: store,
			folderSort: gxui.CBoolean(this.Sort),
			viewConfig: {},
			plugins: [],
			autoScroll: gxui.CBoolean(this.AutoScroll),
			stateful: gxui.CBoolean(this.Stateful),
			stateId: (this.StateId && this.StateId != "") ? this.StateId : this.getUniqueId(),
			stateEvents: gxui.CBoolean(this.Stateful) ? ['itemcollapse', 'itemexpand'] : undefined,
			getState: gxui.CBoolean(this.Stateful) ? this.getState : undefined,
			applyState: gxui.CBoolean(this.Stateful) ? this.applyState(this.LazyLoading) : undefined,
			listeners: this.getListeners()
		};

		// @TODO: Change this to use AutoWidth and AutoHeight
		if (this.Width != 100)
			config.width = this.Width;
		else
			config.autoWidth = true;

		if (this.Height != 100)
			config.height = this.Height;
		else
			config.autoHeight = true;

		if (gxui.CBoolean(this.EnableDragDrop)) {
			config.viewConfig.plugins = {
				ptype: 'treeviewdragdrop',
				appendOnly: gxui.CBoolean(this.AppendOnly),
				ddGroup: ddGroup
			};
			config.viewConfig.listeners = this.getDDListeners();
		}

		if (gxui.CBoolean(this.Multiselection)) {
			config.selModel = {
				mode: 'MULTI',
				listeners: {
					'selectionchange': function (selModel, nodes) {
						this.SelectedNodes = [];
						Ext.each(nodes, function (node) {
							this.SelectedNodes.push(node.data.id);
						}, this);
					},
					scope: this
				}
			};
		}

		if (gxui.CBoolean(this.Editable)) {
			// The column has to be explicitly defined, to set editor properties
			config.columns = [{
				xtype: 'treecolumn',
				dataIndex: 'text',
				flex: 1,
				editor: {
					xtype: 'textfield',
					allowBlank: false,
					selectOnFocus: true,
					cancelOnEsc: true,
					ignoreNoChange: true
				}
			}];

			config.hideHeaders = true;

			config.plugins.push({
				ptype: 'cellediting',
				pluginId: this.getUniqueId() + '-celledit',
				clicksToEdit: 2,
				listeners: {
					'edit': function (editor, e) {
						this.NodeEditText = e.value;
						if (this.NodeEdit) {
							this.NodeEdit();
						}
					},
					scope: this
				}
			});
			config.viewConfig.toggleOnDblClick = false;
		}

		this.m_tree = Ext.create('Ext.tree.Panel', config);
	},

	onRefresh: function () {
		var selNodes = this.m_tree.getSelectionModel().getSelection();
		if (selNodes && selNodes[0]) {
			this.setSelectedNode(selNodes[0]);
		}
	},

	onAfterRender: function () {
		if (gxui.CBoolean(this.ExpandRoot)) {
			Ext.defer(function () {
				this.m_tree.getRootNode().expand(gxui.CBoolean(this.ExpandAll));
			}, 300, this);
		}
	},

	getUnderlyingControl: function () {
		return this.m_tree;
	},

	addToParent: function () {
		return gxui.CBoolean(this.AddToParentGxUIControl);
	},

	createRootNode: function () {
		return {
			id: (this.RootId ? this.RootId : 'ROOT'),
			text: this.RootText,
			icon: (this.RootIcon ? this.RootIcon : undefined),
			cls: (this.RootCls ? this.RootCls : undefined),
			iconCls: (this.RootIconCls ? this.RootIconCls : undefined),
			draggable: false, // disable root node dragging
			children: !this.LazyLoading ? this.cloneNodes(this.Children) : undefined,
			expanded: gxui.CBoolean(this.ExpandRoot)
		};
	},

	createStore: function () {
		var config = {
			root: this.createRootNode(),
			_enableCheckbox: this.EnableCheckbox
		};


		if (this.LazyLoading) {
			config.proxy = {
				type: 'ajax',
				url: this.LoaderURL,
				reader: {
					type: 'json'
				},
				actionMethods: {
					create: "POST",
					read: "POST",
					update: "POST",
					destroy: "POST"
				}
			};
		}

		return config;
	},

	cloneNodes: function (children) {
		children = gxui.clone(children);

		return children.length && children.length > 0 ? children : [children]
	},

	getRowDropData: function (data) {
		if (data && data.gxRow) {
			var gxGrid = data.gxGrid,
				gxRow = data.gxRow,
				gxCols = data.gxColumns;

			var dropData = {};
			for (var i = 0, len = gxCols.length; i < len; i++) {
				var col = gxCols[i],
					colName = col.gxAttName || (col.gxAttId.charAt(0) == "&" ? col.gxAttId.substring(1) : col.gxAttId),
					cell = gxGrid.getPropertiesCell(data.gxGrid.getUnderlyingControl(), gxRow.id, i, true);
				dropData[colName] = cell.value;
			}

			return dropData;
		}

		return null;
	},

	getNodeById: function (nodeId) {
		return this.m_tree.getStore().getNodeById(nodeId);
	},

	getDDListeners: function () {
		return {
			'dragover': function (node, data, overModel, dropPosition) {
				if (this.NodeOver) {
					// Set UC properties before fireing the event
					this.DropTarget = overModel.data.id;
					this.DropPoint = dropPosition;

					if (data.gxGrid) {
						this.DropData = this.getRowDropData(data);
					}
					else {
						this.DropNode = data.records[0].data.id;
					}

					this.DropAllowed = true;

					
					this.NodeOver();

					return this.DropAllowed;
				}
				return true;
			},

			'beforedrop': function (node, data, overModel, dropPosition, opts) {
				if (data.gxGrid) {
					this.DropTarget = overModel.data.id;
					this.DropPoint = dropPosition;

					this.DropData = this.getRowDropData(data);
					opts.cancelDrop();

					if (this.RowDrop) {
						this.RowDrop();
					}
				}
			},

			'drop': function (node, data, overModel, dropPosition) {
				this.DropTarget = overModel.data.id;
				this.DropPoint = dropPosition;

				if (data.records.length > 0) {
					this.DropNode = data.records[0].data.id;
					if (this.NodeDrop) {
						
						this.NodeDrop();
					}
				}
			},

			scope: this
		};
	},

	getListeners: function () {
		var listeners = {
			'itemclick': function (view, node, item, index, e) {
				var editorPlugin = this.getEditorPlugin();
				var startEdit = (node.data.id == this.SelectedNode) && editorPlugin;
				this.endEdit();
				this.setSelectedNode(node);
				if (!node.data.href) {
					if (this.NotifyContext == "true") {
						this.notifyContext([this.NotifyDataType], { id: node.data.id, text: node.data.text, leaf: node.data.leaf, icon: node.data.icon });
					}
					if (this.Click && (!node.hasChildNodes() || !gxui.CBoolean(this.DisableBranchEvents))) {
						
						this.Click();
					}
				}
				if (startEdit) {
					editorPlugin.startEdit(node, 0);
				}
			},

			'itemdblclick': function (view, node) {
				this.endEdit();
				this.setSelectedNode(node);
				if (this.DoubleClick && (!node.hasChildNodes() || !gxui.CBoolean(this.DisableBranchEvents))) {
					
					this.DoubleClick();
				}
			},

			'checkchange': function (node) {
				if (this.CheckChange) {
					this.setSelectedNode(node);
					
					this.CheckChange();
				}
			},

			scope: this
		};

		if (this.ContextMenu) {
			listeners['itemcontextmenu'] = function (view, node) {
				this.endEdit();
				if (this.ContextMenu) {
					this.setSelectedNode(node);
					this.m_tree.getSelectionModel().select(node);
					
					this.ContextMenu();
				}
			};
		}

		return listeners;
	},

	setSelectedNode: function (node) {
		this.SelectedNode = node.data.id;
		this.SelectedText = node.data.text;
		this.SelectedIcon = node.data.icon;
		this.SelectedNodeData = node.data.data;
		this.SelectedNodeChecked = node.data.checked || false;
	},

	getEditorPlugin: function () {
		return this.m_tree.getPlugin(this.getUniqueId() + '-celledit');
	},

	endEdit: function () {
		var editorPlugin = this.getEditorPlugin();
		if (editorPlugin) {
			editorPlugin.completeEdit();
		}
	},

	getState: function () {
		var nodes = [], state = Ext.tree.Panel.prototype.getState.apply(this, arguments);
		this.getRootNode().eachChild(function (child) {
			//function to store state of tree recursively
			var storeTreeState = function (node, expandedNodes) {
				if (node.isExpanded() && node.childNodes.length > 0) {
					expandedNodes.push(node.getPath("id"));
					node.eachChild(function (child) {
						storeTreeState(child, expandedNodes);
					});
				}
			};
			storeTreeState(child, nodes);
		});

		Ext.apply(state, {
			expandedNodes: nodes
		});

		return state;
	},

	applyState: function (lazyLoading) {
		return function (state) {
			var nodes = state.expandedNodes || [],
				len = nodes.length;

			var expandNodes = Ext.bind(function () {
				for (var i = 0; i < len; i++) {
					if (typeof nodes[i] != 'undefined') {
						this.expandPath(nodes[i], 'id');
					}
				}
				Ext.tree.Panel.prototype.applyState.call(this, state)
			}, this);
			
			this.collapseAll();
			if (lazyLoading) {
				setTimeout(expandNodes, 100);
			}
			else {
				expandNodes();
			}
		};
	},

	findChildNode: function (id, nodes) {
		for (var i = 0, len = nodes.length; i < len; i++) {
			if (nodes[i].id == id) {
				return nodes[i];
			}
			if (nodes[i].children.length > 0) {
				var node = this.findChildNode(id, nodes[i].children);
				if (node) {
					return node;
				}
			}
		}
		return null;
	},

	setNodeProperty: function (nodeId, name, value) {
		var node = this.getNodeById(nodeId);
		if (node) {
			node.set(name, value);
		}
	},

	methods: {
		// Methods
		
		SelectNode: function (nodeId) {
			var node = this.getNodeById(nodeId);
			if (node) {
				this.setSelectedNode(node)
				this.m_tree.getSelectionModel().select(node);
				this.m_tree.expandPath(node.getPath("id"), "id");
			}
		},

		
		SelectNextNode: function () {
			this.m_tree.getSelectionModel().selectNext();
		},

		
		SelectPreviousNode: function () {
			this.m_tree.getSelectionModel().selectPrevious();
		},

		
		DeleteNode: function (nodeId) {
			var node = this.getNodeById(nodeId);
			if (node) {
				node.remove();
			}
		},

		
		ExpandNode: function (nodeId) {
			var node = this.getNodeById(nodeId);
			if (node) {
				node.expand();
			}
		},

		
		ExpandAllNodes: function () {
			this.m_tree.expandAll();
		},

		
		CollapseNode: function (nodeId) {
			var node = this.getNodeById(nodeId);
			if (node) {
				node.collapse();
			}
		},

		
		CollapseAllNodes: function () {
			this.m_tree.collapseAll();
		},

		
		Reload: function (node, expand) {
			var tree = this.m_tree;
			// node can be a TreeNode or a String with the Id of a node. If node is undefined, the root node is reloaded.
			var n = node ? ((typeof node == 'object') ? node : this.getNodeById(node)) : tree.getRootNode();

			if (n) {
				var loadCallback = function (node, initState) {
					node = node || n;
					if (expand || expand === undefined) {
						node.expand();
					}
					if (initState !== false) {
						tree.initState();
					}
				};

				var store = tree.getStore();
				if (this.LazyLoading) {
					var loadCfg = {
						callback: Ext.bind(loadCallback, this),
						node: n
					};

					store.getProxy().url = this.LoaderURL;
					if (store.isLoading())
						Ext.defer(store.load, 500, store, [loadCfg]);
					else
						store.load(loadCfg);
				}
				else {
					if (n == tree.getRootNode()) {
						tree.setRootNode(this.createRootNode());
					}
					else {
						var rawNode = this.findChildNode(node, this.Children),
							newNode = n.parentNode.replaceChild(rawNode, n);
					}
					loadCallback(newNode, false);
				}

				if (this.SelectedNode != undefined) {
					this.SelectNode(this.SelectedNode);
				}
			}
		},

		
		Refresh: function () {
			var tree = this.m_tree;
			tree.setHeight((this.Height != 100) ? this.Height : undefined);
			tree.setWidth((this.Width != 100) ? this.Width : undefined);
			tree.setTitle(this.Title);
			this.Reload(tree.getRootNode(), gxui.CBoolean(this.ExpandRoot));
		},

		
		Show: function () {
			this.m_tree.setVisible(true);
		},

		
		Hide: function () {
			this.m_tree.setVisible(false);
		},

		
		GetNodeParentId: function (nodeId) {
			var node = this.getNodeById(nodeId);
			if (node && node.parentNode) {
				return node.parentNode.data.id;
			}
			return "";
		},

		
		SetNodeData: function (nodeId, nodeData) {
			var node = this.getNodeById(nodeId);
			if (node) {
				node.data.data = nodeData;
			}
		},

		
		GetNodeData: function (nodeId) {
			var node = this.getNodeById(nodeId);
			if (node) {
				return node.data.data;
			}
		},

		
		SetNodeText: function (nodeId, text) {
			this.setNodeProperty(nodeId, 'text', text);
		},


		
		SetNodePropertyBoolean: function (nodeId, name, value) {
			this.setNodeProperty(nodeId, name, value);
		},

		
		SetNodePropertyString: function (nodeId, name, value) {
			this.setNodeProperty(nodeId, name, value);
		},

		
		SetNodePropertyNumber: function (nodeId, name, value) {
			this.setNodeProperty(nodeId, name, value);
		},

		
		StartEdit: function (nodeId, value) {
			var node = this.getNodeById(nodeId),
				editorPlugin;
			if (node) {
				editorPlugin = this.getEditorPlugin();
				if (editorPlugin) {
					if (value !== undefined) {
						editorPlugin.on({
							'beforeedit': function (editor, e) {
								e.value = value;
							},
							single: true
						});
					}
					editorPlugin.startEdit(node, 0);
				}
			}
		},

		
		CancelEdit: function () {
			var editorPlugin = this.getEditorPlugin();
			if (editorPlugin) {
				editorPlugin.cancelEdit();
			}
		},

		
		ClearAllNodes: function () {
			var root = this.m_tree.getRootNode();
			root.removeAll();
			delete root.data.children;
		},

		
		NodeExists: function (nodeId) {
			var node = this.getNodeById(nodeId);
			return (node ? true : false);
		}
	}
});

// isValidDropPoint is overriden to be able to fire dragover event.
Ext.tree.ViewDropZone.override({
	isValidDropPoint: function (node, position, dragZone, e, data) {
		if (this.callOverridden(arguments) === false)
			return false;

		if (this.view.fireEvent('dragover', node, data, this.view.getRecord(node), position, e) === false)
			return false;

		return true;
	}
});

Ext.data.TreeStore.override({
	fillNode: function (node, records) {
		if (records) {
			for (var i = 0, len = records.length; i < len; i++) {
				var record = records[i];
				if (!this._enableCheckbox) {
					delete record.raw.checked;
					record.data.checked = null;
				}
				if (record.raw.leaf === false && record.raw.children && record.raw.children.length == 0)
					delete record.raw.children;
			}
		}
		return this.callOverridden(arguments);
	}
});

Ext.define('Ext.ux.form.field.DateTime', {
	extend: 'Ext.form.FieldContainer',
	mixins: {
		field: 'Ext.form.field.Field'
	},
	alias: 'widget.xdatetime',

	//configurables

	combineErrors: true,
	msgTarget: 'under',
	layout: 'hbox',
	readOnly: false,

	
	dateFormat: 'Y-m-d',
	
	timeFormat: 'H:i:s',
	//    
	//    dateTimeFormat: 'Y-m-d H:i:s',
	
	dateConfig: {},
	
	timeConfig: {},


	// properties

	dateValue: null, // Holds the actual date
	
	dateField: null,
	
	timeField: null,

	initComponent: function () {
		var me = this
            , i = 0
            , key
            , tab;

		me.items = me.items || [];

		me.dateField = Ext.create('Ext.form.field.Date', Ext.apply({
			format: me.dateFormat,
			flex: 1,
			isFormField: false, //exclude from field query's
			submitValue: false
		}, me.dateConfig));
		me.items.push(me.dateField);

		me.timeField = Ext.create('Ext.form.field.Time', Ext.apply({
			format: me.timeFormat,
			flex: 1,
			isFormField: false, //exclude from field query's
			submitValue: false
		}, me.timeConfig));
		me.items.push(me.timeField);

		for (; i < me.items.length; i++) {
			me.items[i].on('focus', Ext.bind(me.onItemFocus, me));
			me.items[i].on('blur', Ext.bind(me.onItemBlur, me));
			me.items[i].on('specialkey', function (field, event) {
				key = event.getKey();
				tab = key == event.TAB;

				if (tab && me.focussedItem == me.dateField) {
					event.stopEvent();
					me.timeField.focus();
					return;
				}

				me.fireEvent('specialkey', field, event);
			});
		}

		me.callParent();

		// this dummy is necessary because Ext.Editor will not check whether an inputEl is present or not
		//		this.inputEl = {
		//			dom: {},
		//			swallowEvent: function () { }
		//		};

		me.initField();
	},

	focus: function () {
		this.callParent();
		Ext.defer(this.dateField.focus, 100, this.dateField);
	},

	onItemFocus: function (item) {
		if (this.blurTask) {
			this.blurTask.cancel();
		}
		this.focussedItem = item;
	},

	onItemBlur: function (item, e) {
		
	},

	getValue: function () {
		var value = null
            , date = this.dateField.getSubmitValue()
            , time = this.timeField.getSubmitValue()
            , format;

		if (date) {
			if (time) {
				format = this.getFormat();
				value = Ext.Date.parse(date + ' ' + time, format);
			} else {
				value = this.dateField.getValue();
			}
		}
		return value;
	},

	getSubmitValue: function () {
		//        var value = this.getValue();
		//        return value ? Ext.Date.format(value, this.dateTimeFormat) : null;

		var me = this
            , format = me.getFormat()
            , value = me.getValue();

		return value ? Ext.Date.format(value, format) : null;
	},

	setValue: function (value) {
		if (Ext.isString(value)) {
			value = Ext.Date.parse(value, this.getFormat()); //this.dateTimeFormat
		}
		this.dateField.setValue(value);
		this.timeField.setValue(value);
	},

	getFormat: function () {
		return (this.dateField.submitFormat || this.dateField.format) + " " + (this.timeField.submitFormat || this.timeField.format);
	},

	// Bug? A field-mixin submits the data from getValue, not getSubmitValue
	getSubmitData: function () {
		var me = this
            , data = null;

		if (!me.disabled && me.submitValue && !me.isFileUpload()) {
			data = {};
			data[me.getName()] = '' + me.getSubmitValue();
		}
		return data;
	},

	isValid: function () {
		return this.dateField.isValid() && this.timeField.isValid();
	}
});


Ext.define('Ext.ux.CheckColumn', {
    extend: 'Ext.grid.column.Column',
    alias: 'widget.checkcolumn',
    
    constructor: function() {
        this.addEvents(
            
            'checkchange'
        );
        this.callParent(arguments);
    },

    
    processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
        if (type == 'mousedown' || (type == 'keydown' && (e.getKey() == e.ENTER || e.getKey() == e.SPACE))) {
            var record = view.panel.store.getAt(recordIndex),
                dataIndex = this.dataIndex,
                checked = !record.get(dataIndex);
                
            record.set(dataIndex, checked);
            this.fireEvent('checkchange', this, recordIndex, checked);
            // cancel selection.
            return false;
        } else {
            return this.callParent(arguments);
        }
    },

    // Note: class names are not placed on the prototype bc renderer scope
    // is not in the header.
    renderer : function(value){
        var cssPrefix = Ext.baseCSSPrefix,
            cls = [cssPrefix + 'grid-checkheader'];

        if (value) {
            cls.push(cssPrefix + 'grid-checkheader-checked');
        }
        return '<div class="' + cls.join(' ') + '">&#160;</div>';
    }
});



Ext.define('Skirtle.grid.AutoWidther', {
	mixins: { observable: 'Ext.util.Observable' },

	pendingCount: 0,

	constructor: function (config) {
		var me = this;

		Ext.apply(me, config);

		me.addEvents('beforecolumnresize', 'columnresize');
		me.mixins.observable.constructor.call(me);
	},

	autoWidthColumn: function (column, config) {
		var me = this,
            grid = me.grid,
            empty = grid.getStore().getCount() === 0,
            els,
            originalColumnElWidth,
            newWidth;

		config = config || {};

		// Don't resize hidden columns. Arguably this an overly simplistic approach but at least it has good performance
		if (column.isHidden() || !grid.rendered) {
			return false;
		}

		if (empty) {
			if (!Ext.isNumber(newWidth = me.getEmptyWidth(column, config))) {
				return false;
			}
		}
		else {
			newWidth = (config.includeHeader && me.calculateHeaderWidth(column, config)) || 1;

			// Can be multiple, e.g. using the grouping feature
			els = me.getColumnResizers(column, config);

			if (els.length) {
				// Reset the styles on the table so it uses auto sizing, unless this has already been done elsewhere
				me.start();

				Ext.each(els, function (el) {
					el = Ext.fly(el);

					
					originalColumnElWidth = el.dom.style.width;
					el.setStyle('width', 'auto');

					newWidth = Math.max(el.getWidth(), newWidth);

					// Put it back the way we found it
					el.setStyle('width', originalColumnElWidth);
				});

				// Put the table back the way we found it
				me.end();
			}
		}

		config.newWidth = newWidth;

		if (me.beforeColumnResize(column, config) === false
            || me.fireEvent('beforecolumnresize', me, column, config) === false) {
			return false;
		}

		// flex takes priority over width, so remove it
		if (column.flex) {
			column.flex = null;
		}

		column.setWidth(config.newWidth, !me.isPending());

		// Required if the column was previously flexed as setWidth thinks the width is already correct
		column.width = config.newWidth;

		me.onColumnResize(column, config);
		me.fireEvent('columnresize', me, column, config);

		return config.newWidth;
	},

	// TODO: Should this be empty?
	beforeColumnResize: function (column, config) {
		if (config.minWidth) {
			config.newWidth = Math.max(config.newWidth, config.minWidth);
		}

		if (config.maxWidth) {
			config.newWidth = Math.min(config.newWidth, config.maxWidth);
		}
	},

	calculateHeaderWidth: function (column, config) {
		// TODO: Verify the need for all the null ternary checks
		var me = this,
            grid = me.grid,
            selector = me.headerCellSelector(column, config),
            triggerSelector = me.headerTriggerSelector(column, config),
            el = grid.getEl(),
            headerCell = el.down(selector),
            trigger = headerCell ? headerCell.down(triggerSelector) : null;

		return headerCell
            ? me.calculateWidth(headerCell) + (trigger ? trigger.getComputedWidth() : 0)
            : false;
	},

	// TODO: Currently only used for the header... is it really necessary?
	calculateWidth: function (cell) {
		var el = Ext.fly(cell);

		return el.getTextWidth() + el.getFrameWidth('lr');
	},

	destroy: function () {
		this.clearListeners();
		this.grid = null;
	},

	// Called when resizing is complete to set the styles on the table elements back to what they were at the start
	end: function () {
		if (! --this.pendingCount) {
			Ext.iterate(this.originalWidths, function (id, width) {
				var tableEl = Ext.fly(id);

				tableEl.setStyle('table-layout', '');
				tableEl.setWidth(width);
			});
		}
	},

	getColumnResizers: function (column, config) {
		// Grab the <th> rows (one per table) that are used to size the columns
		// TODO: can't assume x- prefix
		var els = this.grid.getEl().query('.x-grid-col-resizer-' + column.id);

		// Grouping feature - first table wraps everything and needs to be ignored
		if (els.length > 1 && Ext.fly(els[0]).parent('table').contains(els[1])) {
			els.shift();
		}

		return els;
	},

	getEmptyWidth: function (column, config) {
		if (config.emptyWidth === 'header') {
			return this.calculateHeaderWidth(column, config);
		}

		return config.emptyWidth;
	},

	getTableResizers: function () {
		// TODO: can't assume x- prefix
		var els = this.grid.getView().getEl().query('.x-grid-table-resizer');

		// Grouping feature - first table wraps everything and can be ignored
		if (els.length > 1 && Ext.fly(els[0]).contains(els[1])) {
			els.shift();
		}

		return els;
	},

	headerCellSelector: function (column, config) {
		// TODO: can't assume x- prefix
		// TODO: Should we even use a selector for this?
		return Ext.String.format('#{0} .x-column-header-inner', column.id);
	},

	headerTriggerSelector: function (column, config) {
		// TODO: can't assume x- prefix
		// TODO: Should we even use a selector for this?
		return '.x-column-header-trigger';
	},

	isPending: function () {
		return this.pendingCount !== 0;
	},

	onColumnResize: Ext.emptyFn,

	// Sets the table element for the grid to automatic width... must be reset using endAutoWidth when we're done
	start: function () {
		var me = this;

		if (!me.pendingCount++) {
			me.originalWidths = {};

			Ext.each(me.getTableResizers(), function (tableEl) {
				tableEl = Ext.fly(tableEl);

				me.originalWidths[Ext.id(tableEl)] = tableEl.getWidth();

				tableEl.setStyle({
					'table-layout': 'auto',
					width: 'auto'
				});
			});
		}
	}
});





Ext.define('Skirtle.grid.plugin.AutoWidthColumns', {
	alias: 'plugin.autowidthcolumns',
	extend: 'Ext.AbstractPlugin',
	mixins: { observable: 'Ext.util.Observable' },
	requires: ['Skirtle.grid.AutoWidther'],

	// private
	cfgCopy: ['emptyWidth', 'includeHeader', 'maxWidth', 'minWidth'],

	// The default width for all auto-sized columns when there are no rows
	emptyWidth: 'header',

	// Whether or not the header text should be considered as content in the column
	includeHeader: true,

	//
	pluginId: 'autowidthcolumns',

	init: function (grid) {
		var me = this;

		me.addEvents('beforecolumnresize', 'columnresize');
		me.mixins.observable.constructor.call(me);

		me.bindGrid(grid);
	},

	autoWidthColumn: function (col, configOverrides) {
		var me = this,
            column = Ext.isNumber(col) ? me.getCmp().columns[col] : col,
            config = me.resolveColumnConfig(column, configOverrides),
            width = me.autoWidther.autoWidthColumn(column, config);

		// width can also be false if no resize occurred
		if (width) {
			me.handleSingle(column);
		}

		return width;
	},

	bindGrid: function (grid) {
		var me = this,
            disabled = me.disabled,
            autoWidther;

		// Removes listeners from the current grid, if any
		me.disable();

		me.cmp = grid;

		if (autoWidther = me.autoWidther) {
			autoWidther.destroy();
		}

		if (autoWidther = me.autoWidther = grid ? me.createAutoWidther(grid) : null) {
			me.registerRelayedEvents(autoWidther);
		}

		if (!disabled) {
			// Adds listeners to the new grid
			me.enable();
		}
	},

	createAutoWidther: function (grid) {
		return Ext.create('Skirtle.grid.AutoWidther', Ext.apply({
			grid: grid
		}, this.autoWidtherConfig));
	},

	destroy: function () {
		this.bindGrid(null);
		this.clearListeners();
	},

	disable: function () {
		var me = this,
            grid = me.getCmp();

		if (grid) {
			me.unregisterListeners(grid);
		}

		me.callParent();
	},

	enable: function () {
		var me = this,
            grid = me.getCmp();

		if (me.disabled && grid) {
			me.registerListeners(grid);
		}

		me.callParent();
	},

	getGridViewChangeEvents: function () {
		return ['refresh', 'itemadd', 'itemremove', 'itemupdate'];
	},

	handleSingle: function (column) {
		if (column.autoWidth === 'single') {
			column.autoWidth = false;
		}
	},

	// Handler for the grid's columnshow event
	onGridColumnShow: function (ct, column) {
		if (column.autoWidth) {
			this.autoWidthColumn(column);
		}
	},

	// Handler for the grid view's refresh event
	onGridViewChange: function () {
		this.refresh();
	},

	registerListeners: function (grid) {
		var me = this;

		Ext.each(me.getGridViewChangeEvents(), function (eventName) {
			grid.getView().on(eventName, me.onGridViewChange, me);
		});

		grid.on('columnshow', me.onGridColumnShow, me);
	},

	// TODO: Tidy this up a bit
	registerRelayedEvents: function (autoWidther) {
		var me = this;

		autoWidther.on('beforecolumnresize', function (aw, column, config) {
			return me.fireEvent('beforecolumnresize', me, column, config);
		});

		autoWidther.on('columnresize', function (aw, column, config) {
			return me.fireEvent('columnresize', me, column, config);
		});
	},

	refresh: function () {
		var me = this,
            grid = me.getCmp(),
            columns = grid.columns,
            index = 0,
            len = columns.length,
            updated = false;

		if (!grid.rendered || !grid.getView().rendered) {
			return;
		}

		me.autoWidther.start();

		for (; index < len; ++index) {
			var column = columns[index];

			if (column.autoWidth && me.autoWidthColumn(column)) {
				updated = true;
			}
		}

		me.autoWidther.end();

		if (updated) {
			// TODO: Not always necessary and can be very time-consuming
			grid.headerCt.doLayout();
		}
	},

	resolveColumnConfig: function (column, configOverrides) {
		var config = {},
            me = this;

		if (configOverrides) {
			Ext.apply(config, configOverrides);
		}

		if (Ext.isObject(column.autoWidth)) {
			Ext.applyIf(config, column.autoWidth);
		}

		Ext.each(me.cfgCopy, function (prop) {
			if (!(prop in config)) {
				config[prop] = me[prop];
			}
		});

		return config;
	},

	unregisterListeners: function (grid) {
		var me = this;

		Ext.each(me.getGridViewChangeEvents(), function (eventName) {
			grid.getView().un(eventName, me.onGridViewChange, me);
		});

		grid.un('columnshow', me.onGridColumnShow, me);
	}
});
/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />

Ext.define('gxui.GridExtension.DragDrop', {
	constructor: function (gridUC, grid, cfg) {
		Ext.apply(this, cfg || {});

		this.gridUC = gridUC;
		this.m_grid = grid;

		this.configGridDragZone();
	},

	configGridDragZone: function () {
		var grid = this.m_grid;
		var ddPlugin = grid.getView().getPlugin(this.gridUC.getUniqueId() + '-dd');
		if (ddPlugin && ddPlugin.dragZone) {
			var dragZone = ddPlugin.dragZone;
			dragZone.onInitDrag = Ext.bind(this.initializeDrag, this);
			dragZone.onBeforeDrag = Ext.bind(this.onBeforeDrag, this);
			dragZone.onStartDrag = function () { console.log('onStartDrag', arguments); }
			// If I want to show visual feedback when a row being dragged hovers a valid drop target, the group of valid
			// drop targets must be intialized using a Ext.dd.DropZone. Must be done afterShow, so the target elements
			// exist in the dom.
			this.defineDropTargets();

			dragZone.primaryButtonOnly = gx.lang.gxBoolean(this.PrimaryButtonOnly);
		}
	},

	initializeDrag: function () {
		
		if (this.gridUC.OnInitDrag) {
			this.gridUC.OnInitDrag();
		}

		var ddPlugin = this.m_grid.getView().getPlugin(this.gridUC.getUniqueId() + '-dd');
		if (ddPlugin && ddPlugin.dragZone) {
			var dragZone = ddPlugin.dragZone;
			dragZone.ddel.update(this.DragDropText || ddPlugin.dragText);
			dragZone.proxy.update(dragZone.ddel.dom);
		}
	},

	onBeforeDrag: function (data, e) {
		var dnd = gx.fx.dnd;
		var grid = this.m_grid,
			gridUC = this.gridUC,
			record = grid.getView().getRecord(data.item),
			rowIndex = grid.getView().indexOf(data.item),
			actualRowIndex = gridUC.getActualRowIndex(grid, rowIndex),
			row = gridUC.rows[actualRowIndex],
			dragSource = this.getGxRowDragSource(row);

		if (dragSource) {
			dnd.dragInfo = function () { return ""; }; // Override this function to avoid standard GX dd proxy
			dnd.dragCtrl = data.ddel.dom;
			dnd.dragCtrl.gxId = row.gxId;
			dnd.dragCtrl.gxGrid = gridUC.ownerGrid.containerName;
			dnd.dragCtrl.gxGridName = gridUC.ownerGrid.gridName;
			dnd.dragCtrl.gxDndClassName = dragSource.cssClass;
			// Set the row as the dragged object
			dnd.drag(dragSource.obj, dragSource.types, dragSource.hdl);

			// Set the internal GX row in the data so it can be accessed in beforenodedrop in Treeview.
			data.gxGrid = gridUC;
			data.gxRow = row;
			data.gxColumns = gridUC.columns;
		}
		return true;
	},

	defineDropTargets: function () {
		if (!this.m_dropTargetsCreated) {
			this.m_dropTargets = {};
			var dnd = gx.fx.dnd;
			if (dnd.sources.length > 0) {
				// Get the types of the grids rows
				var types = dnd.sources[0].types;
				Ext.each(dnd.targets, function (t) {
					// If the target types matches the types of the grid rows
					if (gx.fx.matchingTypes(types, t.types)) {
						this.m_dropTargets[t.id] = new Ext.dd.DropTarget(Ext.get(t.id), {
							ddGroup: this.gridUC.DragDropGroup,

							notifyOver: function () {
								return this.dropAllowed;
							},

							notifyDrop: function () {
								return true;
							}

						});
					}
				}, this);
				this.m_dropTargetsCreated = true;
			}
		}
	},

	getGxRowDragSource: function (row) {
		var trId = this.gridUC.getRowGxInternalId(row);

		var dragSource;
		// Find the drag source for the row
		Ext.each(gx.fx.dnd.sources, function (s) {
			if (s.id == trId) {
				dragSource = s;
				return false;
			}
		});

		return dragSource;
	},

	destroy: function () {
		delete this.gridUC;
		delete this.m_grid;

		if (this.m_dropTargets) {
			for (dropTarget in this.m_dropTargets) {
				dropTarget.unreg();
			}
		}
		delete this.m_dropTargets;
	}
});

/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.GridExtension', {
	extend: 'gxui.UserControl',

	onRender: function () {
		var cmConf = this.getColumnsConfig(),
			storeConf = this.getStoreConfig(cmConf.fields),
			smConf = this.getSelModelConfig(),
			viewConf = this.getViewConfig(),
			plugins = this.getPlugins(),
			features = this.getFeatures();

		// mask and unmask methods are overriden to avoid the default GX mask
		this.mask = Ext.emptyFn;
		this.unmask = Ext.emptyFn;

		// create the Grid
		this.m_grid = this.createGridPanel(cmConf, storeConf, smConf, viewConf, plugins, features);
	},

	onRefresh: function () {
		if ((!this.editable && this.isEditable(true)) || this.columnModelChanged(this.m_grid)) {
			this.m_grid.destroy();
			this.forceRendering();
			this.renderControl();
			this.keepSelection(this.m_grid);
		}
		else {
			var grid = this.m_grid,
				view = grid.getView();

			// Toggle summary row
			if (gx.lang.gxBoolean(this.Grouping)) {
				var groupingFeature = view.getFeature(this.getUniqueId() + '-grouping');
				if (groupingFeature && groupingFeature.ftype == 'groupingsummary')
					groupingFeature.toggleSummaryRow(gx.lang.gxBoolean(this.ShowGroupingSummary));
			}

			grid.getStore().loadRawData(this.properties);

			this.updatePagingToolbar(grid.getDockedComponent('toolbar'));

			this.keepSelection(grid);

			if (gx.lang.gxBoolean(this.gxAllowCollapsing)) {
				if (gx.lang.gxBoolean(this.gxCollapsed)) {
					grid.collapse();
				}
				else {
					grid.expand();
				}
			}

			if (this.Visible != undefined) {
				if (gx.lang.gxBoolean(this.gxVisible) && !grid.isVisible()) {
					grid.show();
				}
				else {
					if (!gx.lang.gxBoolean(this.gxVisible) && grid.isVisible()) {
						grid.hide();
					}
				}
			}

			if (!grid.ownerCt) {
				if (this.gxHeight && grid.getBox().height != this.gxHeight) {
					grid.setHeight(this.gxHeight);
				}

				if (this.gxWidth && grid.getBox().width != this.gxWidth) {
					grid.setWidth(this.gxWidth);
				}
			}

			grid.setTitle(this.Title);
		}
	},

	onAfterRender: function () {
		// D&D listeners:
		if (gx.lang.gxBoolean(this.ownerGrid.defaultDragable)) {
			this.m_DD = Ext.create('gxui.GridExtension.DragDrop', this, this.m_grid, {
				DragDropText: this.DragDropText,
				PrimaryButtonOnly: this.PrimaryButtonOnly
			});
		}
	},

	onDestroy: function () {
		if (this.m_DD) {
			this.m_DD.destroy();
		}
		gxui.GridExtension.superclass.onDestroy.call(this);
	},

	getUnderlyingControl: function () {
		return this.m_grid;
	},

	addToParent: function () {
		return gx.lang.gxBoolean(this.AddToParentGxUIControl);
	},

	createGridPanel: function (cmConf, storeConf, smConf, viewConf, plugins, features) {
		this.gxWidth = parseInt(this.gxWidth);
		this.gxHeight = parseInt(this.gxHeight);
		var config = {
			id: this.getUniqueId(),
			features: features,
			plugins: plugins,
			store: storeConf,
			columns: cmConf.columns,
			viewConfig: viewConf,
			selType: smConf.selType,
			selModel: smConf.selModel,
			dockedItems: [],
			//cls: this.gx.CssClass,
			forceFit: gx.lang.gxBoolean(this.ForceFit),
			enableColumnHide: gx.lang.gxBoolean(this.EnableColumnHide),
			enableColumnMove: gx.lang.gxBoolean(this.EnableColumnMove),
			enableLocking: gx.lang.gxBoolean(this.EnableColumnLocking),
			collapsible: gx.lang.gxBoolean(this.gxAllowCollapsing),
			collapsed: gx.lang.gxBoolean(this.gxCollapsed),
			header: gx.lang.gxBoolean(this.gxAllowCollapsing),
			height: this.gxHeight ? this.gxHeight : undefined,
			width: this.gxWidth ? this.gxWidth : undefined,
			title: this.Title ? this.Title : undefined,
			listeners: this.gridListeners(),
			stateful: gx.lang.gxBoolean(this.Stateful),
			stateId: this.StateId || undefined
		};

		if (gx.lang.gxBoolean(this.UseThemeClasses)) {
			config.cls = (config.cls || '') + ' ' + this.gxCssClass;
		}

		if (this.usePagingToolbar()) {
			config.dockedItems.push(this.getPagingToolbarConfig());
		}

		var grid = Ext.create('Ext.grid.GridPanel', config);

		return grid;
	},

	getPlugins: function () {
		var plugins = [{ ptype: 'autowidthcolumns'}];

		if (this.isEditable()) {
			var editingPlugin = {
				clicksToEdit: parseInt(this.ClicksToEdit),
				listeners: {
					'edit': this.afterEditHandler,
					'beforeedit': this.beforeEditHandler,
					scope: this
				}
			};

			plugins.push(editingPlugin);

			if (this.EditModel == 'CellEditModel') {
				editingPlugin.ptype = 'cellediting';
				editingPlugin.pluginId = this.getUniqueId() + '-celledit';
			}
			else {
				editingPlugin.ptype = 'rowediting';
				editingPlugin.pluginId = this.getUniqueId() + '-rowedit';
			}
		}

		return plugins;
	},

	getFeatures: function () {
		var features = [];
		if (gx.lang.gxBoolean(this.Grouping)) {
			features.push({
				id: this.getUniqueId() + '-grouping',
				ftype: gx.lang.gxBoolean(this.ShowGroupingSummary) ? 'groupingsummary' : 'grouping',
				groupHeaderTpl: this.GroupTemplate
			});
		}
		return features;
	},

	getSelModelConfig: function () {
		if (this.isEditable() && this.EditModel == 'CellEditModel') {
			return {
				selType: 'cellmodel'
			};
		}
		else if (this.SelectionModel == 'CheckBoxSelectionModel')
			return {
				selType: 'checkboxmodel',
				selModel: {
					injectCheckbox: 'first',
					mode: 'SINGLE'
				}
			};
		else
			return {
				selType: 'rowmodel',
				selModel: {
					mode: 'SINGLE'
				}
			};
	},

	getViewConfig: function () {
		var viewConf = {
			plugins: [],
			disableSelection: !gx.lang.gxBoolean(this.gxAllowSelection),
			trackOver: gx.lang.gxBoolean(this.gxAllowHovering),
			stripeRows: (this.gxTitleBackstyle == gx.grid.styles.report),
			enableTextSelection: gx.lang.gxBoolean(this.EnableTextSelection)
		};

		if (gx.lang.gxBoolean(this.ownerGrid.defaultDragable) && gx.lang.gxBoolean(this.gxAllowSelection))
			viewConf.plugins.push({
				pluginId: this.getUniqueId() + '-dd',
				ptype: 'gridviewdragdrop',
				enableDrop: false,
				ddGroup: this.DragDropGroup || "",
				dragText: this.DragDropText
			});

		return viewConf;
	},

	getColumnsConfig: function () {
		var columns = this.columns;

		var conf = {
			headers: {},
			columns: [],
			fields: []
		};

		for (var i = 0, len = this.columns.length; i < len; i++) {
			this.mapColumn(this.columns[i], i, conf);
		}

		delete conf.headers;

		return conf;
	},

	getColumnType: function (col) {
		if (col.gxControl.type == gx.html.controls.types.checkBox)
			return 'boolean';

		switch (col.type) {
			case gx.types.numeric:
				return "float";
			case gx.types.bool:
				return "boolean";
			case gx.types.date:
				return "date";
			case gx.types.dateTime:
				return "date";
			default:
				return "string";
		};
	},

	converter: {
		date: function (v) {
			var gxdate;
			if (typeof v == 'string' || v instanceof Date)
				gxdate = new gx.date.gxdate(v);
			else if (v instanceof gx.date.gxdate)
				gxdate = v
			gxdate.Value.gxdate = gxdate;
			return gxdate.Value;
		},

		checkBox: function (v, record, col) {
			if (typeof v == 'boolean')
				return v;
			if (v == col.gxChecked)
				return true;
			return false;
		}
	},

	findBcColumnName: function (bc, vStruct) {
		for (var m in bc) {
			if (bc[m] === vStruct)
				return m;
			if (bc[m].gxgrid || bc[m].ctrltype)
				continue;
			if (typeof (bc[m]) === 'object')
				return this.findBcColumnName(bc[m], vStruct);
		}
		return undefined;
	},

	resolveColumnName: function (col) {
		var ownerGrid = this.ownerGrid,
			boundColName = ownerGrid.boundedCollName,
			parentObject = this.parentGxObject,
			gridBCs = parentObject.GridBCs,
			gxControl = col.gxControl;

		if (boundColName) {
			if (gridBCs) {
				for (var m in gridBCs) {
					if (typeof (gridBCs[m]) === 'object' && gridBCs[m].gxvar == boundColName) {
						return this.findBcColumnName(gridBCs[m], gxControl.vStruct || parentObject.getValidStructFld(gxControl.column.htmlName)) || col.gxAttName || col.gxAttId;
					}
				}
			}

			// WA for Evo1, to be able to group complex SDT bound grids. As it's not possible to get the column name, the control name in upper case is used instead.
			return col.htmlName;
		}

		return col.gxAttName || col.gxAttId;
	},

	mapColumn: function (col, i, conf) {
		var GE = gxui.GridExtension, controlTypes = gx.html.controls.types;

		if (gx.lang.gxBoolean(col.visible)) {
			var colType = this.getColumnType(col),
				converter,
				dataIndex = this.resolveColumnName(col);

			if (colType == 'date')
				converter = this.converter.date;
			else if (col.gxControl.type == controlTypes.checkBox)
				converter = this.converter.checkBox;

			conf.fields.push({
				name: dataIndex,
				mapping: function (i, converter) {
					return function (obj) {
						var hasReturn = obj[i].grid.instanciateRow(obj[i].gridRow);
						var value;
						if (hasReturn && obj[i].column.gxControl.type != gx.html.controls.types.checkBox) {
							var bkpObj = gx.O;
							var pO = obj[i].grid.parentObject;
							gx.setGxO(pO.CmpContext, pO.IsMasterPage);
							value = pO.GXValidFnc[obj[i].column.gxId].val();
							gx.setGxO(bkpObj.CmpContext, bkpObj.IsMasterPage);
						}
						else
							value = obj[i].value;
						if (typeof value == "string")
							value = gx.text.trim(value);

						if (converter)
							return converter(value);
						return value;
					};
				} (i),
				type: colType,
				convert: converter ? Ext.bind(converter, this, [col], true) : undefined
			});

			var colWidth = undefined,
				colFlex = undefined;
			if (col.gxControl.type != controlTypes.comboBox) {
				if (!gx.lang.gxBoolean(col.AutoExpand)) {
					if (col.gxWidthUnit == "px")
						colWidth = col.width || undefined;
					else if (col.gxWidthUnit == "%")
						colFlex = col.width / 100 || undefined;
				}
				else {
					colFlex = 1;
				}
			}

			// WA because the width is not present in the column
			if (!colWidth && col.gxControl.type == controlTypes.comboBox) {
				colWidth = col.gxControl.width || undefined;
			}

			var colCfg = {
				xtype: GE.ColumnRenderers.get(col),
				id: this.getUniqueId() + '-col-' + col.htmlName,
				dataIndex: dataIndex,
				header: col.title,
				width: colWidth,
				flex: colFlex,
				hidden: gx.lang.gxBoolean(col.Hidden || false),
				align: col.align,
				hideable: gx.lang.gxBoolean(col.Hideable),
				menuDisabled: gx.lang.gxBoolean(col.MenuDisabled),
				resizable: gx.lang.gxBoolean(col.Resizable) ? undefined : false,
				sortable: gx.lang.gxBoolean(col.Sortable),
				locked: gx.lang.gxBoolean(col.Lock) || undefined,
				checkedValue: col.gxChecked,
				uncheckedValue: col.gxUnChecked,
				gxGrid: this,
				gxColumn: col,
				actualColIndex: i
			};

			var minWidth = parseInt(this.MinColumnWidth);
			if (minWidth)
				colCfg.minWidth = minWidth;

			if (colWidth === undefined && colFlex === undefined)
				colCfg.autoWidth = true;

			if (colCfg.width === undefined) {
				delete colCfg.width;
			}

			if (colCfg.flex === undefined) {
				delete colCfg.flex;
			}

			if (col.gxColumnClass) {
				colCfg.cls = col.gxColumnClass;
				colCfg.tdCls = col.gxColumnClass;
			}

			if (col.SummaryType != 'none')
				colCfg.summaryType = col.SummaryType;

			if (col.HeaderGroup) {
				var hGroup = conf.headers[col.HeaderGroup];
				if (!hGroup) {
					hGroup = {
						header: col.HeaderGroup,
						columns: []
					};
					conf.headers[col.HeaderGroup] = hGroup;
					conf.columns.push(hGroup);
				}
				hGroup.columns.push(colCfg);
			}
			else
				conf.columns.push(colCfg);
		}
	},

	getStoreId: function () {
		return this.getUniqueId() + '-store';
	},

	getStoreConfig: function (fields) {
		var remoteSort = gx.lang.gxBoolean(this.RemoteSort);
		var storeConfig = {
			storeId: this.getStoreId(),
			remoteSort: remoteSort,
			fields: fields,
			data: this.properties,
			clearOnPageLoad: true,
			proxy: {
				type: 'gxui.memory'
			},
			listeners: {
				'groupchange': function (store) {
					// Remember the GroupField selected by the user.
					if (gx.lang.gxBoolean(this.Grouping)) {
						this.GroupField = store.groupField;
					}
				},
				scope: this
			}
		};

		if (this.pageSize > 0)
			storeConfig.pageSize = this.pageSize;

		if (this.SortField) {
			storeConfig.sorters = {
				property: this.SortField,
				direction: this.SortOrder || "ASC"
			};
		}

		if (gx.lang.gxBoolean(this.Grouping)) {
			if (!storeConfig.sorters) {
				storeConfig.sorters = {
					property: this.GroupField
				};
			}
			storeConfig.groupField = this.GroupField;
		}
		return storeConfig;
	},

	usePagingToolbar: function () {
		return this.hasPagingButtons() || !!this.OnFirstPage || !!this.OnPreviousPage || !!this.OnNextPage || !!this.OnLastPage;
	},

	getPagingToolbarConfig: function () {
		var items = [],
			usePaging = this.hasPagingButtons();

		
		if (usePaging || this.OnFirstPage) {
			items.push({
				itemId: 'first',
				tooltip: this.FirstText,
				overflowText: this.FirstText,
				iconCls: "x-tbar-page-first",
				disabled: usePaging && this.isFirstPage(),
				handler: this.OnFirstPage || Ext.bind(this.goToPage, this, ["first"]),
				scope: this
			});
		}

		
		if (usePaging || this.OnPreviousPage) {
			items.push({
				itemId: 'previous',
				tooltip: this.PreviousText,
				overflowText: this.PreviousText,
				iconCls: "x-tbar-page-prev",
				disabled: usePaging && this.isFirstPage(),
				handler: this.OnPreviousPage || Ext.bind(this.goToPage, this, ["prev"]),
				scope: this
			});
		}

		if (usePaging || ((this.OnFirstPage || this.OnPreviousPage) && (this.OnNextPage || this.OnLastPage))) {
			items.push("-");
		}

		
		if (usePaging || this.OnNextPage) {
			items.push({
				itemId: 'next',
				tooltip: this.NextText,
				overflowText: this.NextText,
				iconCls: "x-tbar-page-next",
				disabled: usePaging && this.isLastPage(),
				handler: this.OnNextPage || Ext.bind(this.goToPage, this, ["next"]),
				scope: this
			});
		}

		
		if (usePaging || this.OnLastPage) {
			items.push({
				itemId: 'last',
				tooltip: this.LastText,
				overflowText: this.LastText,
				iconCls: "x-tbar-page-last",
				disabled: usePaging && this.isLastPage(),
				handler: this.OnLastPage || Ext.bind(this.goToPage, this, ["last"]),
				scope: this
			});
		}

		if (usePaging || this.OnFirstPage || this.OnPreviousPage || this.OnNextPage || this.OnLastPage) {
			items.push("-");
		}

		items.push({
			itemId: 'refresh',
			tooltip: this.RefreshText,
			overflowText: this.RefreshText,
			iconCls: Ext.baseCSSPrefix + 'tbar-loading',
			handler: this.refreshGrid,
			scope: this
		});

		if (this.StatusText) {
			items.push('->');
			items.push({
				itemId: 'status',
				xtype: 'tbtext',
				text: this.StatusText,
				overflowText: this.StatusText
			});
		}

		return {
			itemId: 'toolbar',
			xtype: 'toolbar',
			dock: 'bottom',
			items: items
		};
	},

	updatePagingToolbar: function (tb) {
		if (tb) {
			var usePaging = this.hasPagingButtons(),
				first = tb.child('#first'),
				previous = tb.child('#previous'),
				next = tb.child('#next'),
				last = tb.child('#last'),
				status = tb.child('#status');

			if (usePaging) {
				if (first) {
					first.setDisabled(this.isFirstPage());
				}
				if (previous) {
					previous.setDisabled(this.isFirstPage());
				}
				if (next) {
					next.setDisabled(this.isLastPage());
				}
				if (last) {
					last.setDisabled(this.isLastPage());
				}
			}
			if (status) {
				status.setText(this.StatusText);
			}
		}
	},

	setSelectedRow: function (rowIndex) {
		// Set row as selected
		this.SelectedRow = rowIndex + 1;
		var actualRowIndex = this.getActualRowIndex(this.m_grid, rowIndex);
		this.selectRow(actualRowIndex);
	},

	keepSelection: function (grid) {
		if (this.SelectedRow >= 1) {
			grid.getSelectionModel().select(this.SelectedRow - 1);
			var isLoading = this.ownerGrid.isLoading;
			this.ownerGrid.isLoading = true;
			this.selectRow(this.SelectedRow - 1);
			this.ownerGrid.isLoading = isLoading;
			return false;
		}
	},

	gridListeners: function () {
		return {
			'itemclick': function (view, record, el, rowIndex, e) {
				var actualRowIndex = this.getActualRowIndex(view.panel, rowIndex);
				var row = this.rows[actualRowIndex];
				if (row) {
					this.setSelectedRow(rowIndex);
					// Set context
					if (this.ownerGrid.defaultSetsContext) {
						var setter = this.getGxRowContextSetter(row);
						if (setter) {
							this.notifyContext(setter.types, setter.hdl.call(setter.obj, row));
						}
					}
				}
			},

			'cellclick': function (view, cellEl, columnIndex, record, rowEl, rowIndex, e) {
				var cell = this.getPropertiesCell(view.panel, rowIndex, columnIndex, false);

				if (this.isCellEventEnabled(cell))
					this.fireCellClickEvent(rowIndex, columnIndex);
			},

			'itemcontextmenu': function (view, record, rowEl, rowIndex, e) {
				if (this.ContextMenu) {
					this.setSelectedRow(rowIndex);
					this.ContextMenu();
					e.preventDefault();
				}
			},

			'sortchange': function (ct, column, direction) {
				if (this.m_grid) {
					var remoteSort = gx.lang.gxBoolean(this.RemoteSort);

					this.SortField = column.dataIndex;
					this.SortOrder = direction;
					if (remoteSort) {
						// Remember the SortField and SortOrder selected by the user.
						this.m_grid.saveState();
					}

					
					if (this.OnSortChange) {
						this.OnSortChange();
					}
					else {
						if (remoteSort) {
							Ext.defer(this.goToPage, 30, this, ["FIRST"]);
						}
					}

					return !remoteSort;
				}
			},

			'columnresize': function () {
				if (!this.fixingWidth)
					this.fixGridWidth(this.m_grid);
			},

			'beforestaterestore': function (grid, state) {
				if (gx.lang.gxBoolean(this.RemoteSort)) {
					delete state.sort;
				}
				return true;
			},

			'afterlayout': function (grid) {
				// Correct width when it isn't specified, to behave as standard GX grid.
				if (!this.fixingWidth)
					this.fixGridWidth(grid);
			},

			'beforestatesave': function () {
				// Avoid state save when the control is loaded for the first time, as an onResize event
				// is firing an unnecesary state save after rendering the control.
				if (!this.canSaveState) {
					this.canSaveState = true;
					return false;
				}
			},

			scope: this
		};
	},

	isEditable: function (force) {
		if (this.editable === undefined || force) {
			var editable = false;
			for (var i = 0, rows = this.properties.length; i < rows; i++) {
				for (var j = 0, cols = this.properties[i].length; j < cols; j++) {
					editable = editable || this.isCellEditable(this.properties[i][j]);
					if (editable) {
						this.editable = editable;
						return this.editable;
					}
				}
			}
		}

		return this.editable;
	},

	isCellEditable: function (cell) {
		return !gx.lang.gxBoolean(cell.readOnly) && gx.lang.gxBoolean(cell.enabled);
	},

	getActualColumnIndex: function (grid, colIndex) {
		var column;
		if (grid.columnManager) {
			column = grid.columnManager.getHeaderAtIndex(colIndex);
		}
		else {
			column = grid.columns[colIndex];
		}
		
		if (column)
			return column.actualColIndex;
		return -1;
	},

	getActualRowIndex: function (grid, rowIndex) {
		return grid.getStore().getAt(rowIndex).raw[0].row.id;
	},

	getPropertiesCell: function (grid, rowIndex, columnIndex, isActualColIndex) {
		var actualColIndex = columnIndex;
		if (!isActualColIndex) {
			actualColIndex = this.getActualColumnIndex(grid, columnIndex);
		}
		var record = grid.getStore().getAt(rowIndex);
		if (record) {
			return record.raw[actualColIndex];
		}
		return null;
	},

	goToPage: function (page) {
		if (typeof page == "string") {
			if (this.ownerGrid.pagingCommand) {
				this.ownerGrid.pagingCommand(page.toUpperCase());
				return;
			}
			this.UnSelectRows();
			if (this.changeGridPage) {
				this.changeGridPage(page.toUpperCase());
				return;
			}

			this.goToPage_Internal(page.toUpperCase());
		}
	},

	goToPage_Internal: function (pagingDirection) {
		var hiddenName = this.gxGridName.toUpperCase() + "PAGING",
			ownerGrid = this.ownerGrid,
			eventName = '',
			gridId;
		if (pagingDirection) {
			if (gx.pO.fullAjax) {
				gx.setGxO(this.parentGxObject);
				eventName = "E" + ownerGrid.realGridName.toUpperCase() + "_" + pagingDirection + "PAGE" + (ownerGrid.isMasterPageGrid ? "_MPAGE" : "");
				if (ownerGrid.parentGrid) {
					gridId = ownerGrid.parentGrid.gridId;
				}
			}
			else {
				gx.fn.setHidden(this.gxCmpContext + hiddenName, pagingDirection);
				eventName = this.gxCmpContext + "E" + hiddenName + '.';
			}
			gx.evt.execEvt(undefined, undefined, eventName, gx.evt.dummyCtrl, gridId);
		}
	},

	refreshGrid: function () {
		var og = this.ownerGrid, po = this.ParentObject;
		var bkpObj = gx.O;
		gx.setGxO(po.CmpContext, po.IsMasterPage);
		if (og.parentObject.autoRefresh && og.refreshVars.length > 0) {
			og.callAsyncRefresh(og.getRefreshParmsUrl())
		}
		else {
			po.executeServerEvent('RFR', true);
		}
		gx.setGxO(bkpObj.CmpContext, bkpObj.IsMasterPage);
	},

	getRowGxInternalId: function (row) {
		return this.ownerGrid.containerName + 'Row_' + row.gxId;
	},

	getGxRowContextSetter: function (row) {
		var trId = this.getRowGxInternalId(row);

		var setter;
		// Find the context source for the row
		Ext.each(gx.fx.ctx.setters, function (s) {
			if (s.id == trId) {
				setter = s;
				return false;
			}
		}, this);

		return setter;
	},

	isCellEventEnabled: function (cell) {
		var gxControlTypes = gx.html.controls.types;
		return cell.type == gxControlTypes.checkBox || (cell.type == gxControlTypes.image && cell.enabled && (cell.readOnly === undefined || cell.readOnly === true)) || (cell.type != gxControlTypes.image && !cell.enabled);
	},

	fireCellClickEvent: function (rowIndex, columnIndex) {
		var grid = this.m_grid;
		var actualColIndex = this.getActualColumnIndex(grid, columnIndex);
		var actualRowIndex = this.getActualRowIndex(grid, rowIndex);
		var cell = this.getPropertiesCell(grid, actualRowIndex, actualColIndex, true);
		if (this.executeEvent) {
			this.executeEvent(actualColIndex, actualRowIndex);
		}
	},

	fireCellIsValidEvent: function (rowIndex, columnIndex) {
		var grid = this.m_grid,
			actualColIndex = this.getActualColumnIndex(grid, columnIndex),
			actualRowIndex = this.getActualRowIndex(grid, rowIndex),
			cell = this.getPropertiesCell(grid, actualRowIndex, actualColIndex, true),
			gxO = this.parentGxObject,
			vStruct = cell.vStruct || gxO.GXValidFnc[cell.column.gxId]

		if (this.executeIsValid) {
			this.executeIsValid(actualColIndex, actualRowIndex);
		}
		else {
			// For older GX versions (executeIsValid method became available in Evo3U3)
			if (vStruct && vStruct.isvalid) {
				ctrlRow = cell.row.gxId;
				this.ownerGrid.instanciateRow(ctrlRow);
				gxO[vStruct.isvalid].call(gxO);
			}
		}
	},

	fireCellValidation: function (rowIndex, columnIndex, cell) {
		if (this.executeValidate) {
			this.executeValidate(rowIndex, columnIndex, cell.value);
		}
	},

	getSelectedRow: function () {
		return this.SelectedRow;
	},

	beforeEditHandler: function (grid, e) {
		var cell = e.record.raw[e.column.actualColIndex];
		return cell.enabled;
	},

	afterEditHandler: function (editor, e) {
		var actualColIndex = this.getActualColumnIndex(e.grid, e.colIdx),
			cell = this.getPropertiesCell(e.grid, e.rowIdx, actualColIndex, true),
			gxControl = cell.column.gxControl,
			controlTypes = gx.html.controls.types,
			columns = e.grid.columns;

		if (this.EditModel == 'CellEditModel') {
			this.setCellValue(cell, e.value);

			if (gxControl.vStruct && gxControl.vStruct.gxsgprm) {
				this.fireCellValidation(e.rowIdx, e.colIdx, cell);
			}

			// Fire cell click event
			if (gxControl.type == controlTypes.checkBox || gxControl.type == controlTypes.comboBox) {
				this.fireCellClickEvent(e.rowIdx, e.colIdx)
			}

			if (e.originalValue != e.value) {
				this.fireCellIsValidEvent(e.rowIdx, e.colIdx);
			}
		}
		else {
			for (var i = 0, len = columns.length; i < len; i++) {
				actualColIndex = this.getActualColumnIndex(e.grid, i),
				cell = this.getPropertiesCell(e.grid, e.rowIdx, actualColIndex, true);

				var value = e.record.getChanges()[columns[i].dataIndex];

				if (value !== undefined) {
					this.setCellValue(cell, value);
				}

				if (gxControl.vStruct && gxControl.vStruct.gxsgprm) {
					this.fireCellValidation(e.rowIdx, e.colIdx, cell);
				}
			}
		}

		if (gx.lang.gxBoolean(this.ShowGroupingSummary))
			e.grid.getView().refresh();
	},


	setCellValue: function (cell, value) {
		var gxControl = cell.column.gxControl,
			controlTypes = gx.html.controls.types;

		cell.value = value;

		if (gxControl.type == controlTypes.checkBox) {
			if (cell.column.type != gx.types.bool)
				cell.value = value ? cell.column.gxChecked : cell.column.gxUnChecked;
		}

		if (value instanceof Date) {
			var gxdate = value.gxdate;
			cell.value = gxdate.getString();
			if (cell.column.type == gx.types.dateTime) {
				gxdate.HasTimePart = true;
				var validStruct = gxControl.vStruct,
					nDec = validStruct.dec;
				cell.value += ' ' + gxdate.getTimeString(nDec > 3, nDec == 8, nDec > 1);
			}
		}

		if (typeof (value) == "number") {
			cell.value = gxui.GridExtension.Column.prototype.formatNumber(value, gxControl.vStruct);
		}
	},

	fixGridWidth: function (grid) {
		if (!grid.ownerCt) {
			var columns = grid.columns,
				width = grid.lockable ? 3 : 2;
			if (!this.gxWidth) {
				for (var i = 0, len = columns.length; i < len; i++) {
					var col = Ext.getCmp(columns[i].id);
					width += col.getWidth();
				}
				this.fixingWidth = true;
				grid.setWidth(width);
				this.fixingWidth = false;
			}
		}
	},

	columnModelChanged: function (grid) {
		var newColumnModel = this.getColumnsConfig(),
			newColumns = newColumnModel.columns,
			oldColumns = grid.initialConfig.columns,
			newCol,
			oldCol,
			properties = ['hideable', 'hidden', 'locked', 'resizable', 'sortable', 'header'];

		if (oldColumns.length != newColumns.length) {
			return true;
		}

		for (var i = 0, len = oldColumns.length; i < len; i++) {
			oldCol = oldColumns[i];
			newCol = newColumns[i];
			if (oldCol && newCol) {
				if (oldCol.dataIndex != newCol.dataIndex) {
					return true;
				}
				for (var j = 0, propLen = properties.length; j < propLen; j++) {
					if (oldCol[properties[j]] != newCol[properties[j]] && newCol[properties[j]] !== undefined) {
						return true;
					}
				}
			}
			else {
				return true;
			}
		}
	},

	getEditorPlugin: function () {
		var grid = this.m_grid,
				pluginId = grid.id + (this.EditModel == 'CellEditModel' ? '-celledit' : '-rowedit');
		return grid.getPlugin(pluginId);
	},

	methods: {
		// Methods
		
		SelectRow: function (rowIndex) {
			// Row index is 1 based, for compatibility with GeneXus criteria
			if (rowIndex) {
				this.setSelectedRow(rowIndex - 1);
				this.m_grid.getSelectionModel().selectRow(rowIndex - 1);
			}
		},

		UnSelectRows: function () {
			if (this.SelectedRow != undefined) {
				delete this.SelectedRow;
			}
			this.m_grid.getSelectionModel().deselectAll();
		}
	}
});

Ext.define('gxui.data.proxy.Memory', {
	extend: 'Ext.data.proxy.Memory',
	alias: 'proxy.gxui.memory',
	alternateClassName: 'gxui.data.MemoryProxy',

	reader: {
		type: 'json',
		createAccessor: function (i) {
			return function (obj) {
				if (i == 'id')
					return undefined;
			};
		},
		totalProperty: undefined,
		successProperty: undefined,
		idProperty: undefined
	}
});

/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />

Ext.define('gxui.GridExtension.Column', function () {
	var pictureHelperRegex,
		blankWhenZeroRegex = /^Z+(?:\.Z+)?$/;

	return {
		extend: 'Ext.grid.column.Column',
		alias: 'widget.gxui.column',

		constructor: function (config) {
			this.callParent([config]);
			this.renderer = Ext.bind(this.renderer, this);
			this.editor = this.defineEditor(this.gxColumn, this.actualColIndex);
		},

		destroy: function () {
			delete this.gxGrid;
			delete this.gxColumn;
			this.callParent(arguments);
		},

		mapDateFormat: function () {
			switch (gx.dateFormat) {
				case 'MDY':
					return "m/d/y";
				case 'DMY':
					return "d/m/y";
				case 'MDY4':
					return "m/d/Y";
				case 'DMY4':
					return "d/m/Y";
				case 'YMD':
					return "y/m/d";
				default:
					return "Y/m/d";
			}
		},

		mapTimeFormat: function (gxColumn) {
			if (gxColumn.gxControl.vStruct) {
				var nDec = gxColumn.gxControl.vStruct.dec,
				minutes = nDec > 3,
				seconds = nDec == 8,
				hours = nDec > 1;

				if (gx.timeFormat == 12) {
					if (hours && minutes && seconds)
						return "h:i:s A";

					if (hours && minutes)
						return "h:i A";

					if (hours)
						return "h A";
				}
				else {
					if (hours && minutes && seconds)
						return "H:i:s";

					if (hours && minutes)
						return "H:i";

					if (hours)
						return "H";
				}
			}

			if (gx.timeFormat == 12) {
				return "h:i A";
			}

			return "H:i";
		},

		defineEditor: function (gxColumn, actualColIndex) {
			var types = gx.types;
			switch (gxColumn.type) {
				case types.numeric:
					var colData = this.gxGrid.ParentObject.GXValidFnc[gxColumn.gxId];
					return {
						xtype: 'numberfield',
						allowDecimals: colData.dec > 0 ? true : false,
						minValue: colData.sign ? Number.NEGATIVE_INFINITY : 0,
						decimalPrecision: colData.dec,
						decimalSeparator: gx.decimalPoint,
						enforceMaxLength: true,
						maxLength: colData.len,
						maxValue: Math.pow(10, colData.len - colData.dec - (colData.dec > 0 ? 1 : 0)) - (colData.dec > 0 ? 1 / Math.pow(10, colData.dec) : 0)
					};

				case types.date:
					return {
						xtype: 'datefield',
						format: this.mapDateFormat()
					};

				case types.dateTime:
					return {
						xtype: 'xdatetime',
						dateFormat: this.mapDateFormat(),
						timeFormat: this.mapTimeFormat(gxColumn)
					};

				default:
					if (gxColumn.gxControl.type == gx.html.controls.types.multipleLineEdit)
						return {
							xtype: 'textareafield',
							maxLength: gxColumn.gxControl.maxLength
						};

					return {
						xtype: 'textfield',
						maxLength: gxColumn.gxControl.maxLength
					};
			}
		},

		formatNumber: function (value, colData) {
			var extUtilFormat = Ext.util.Format;
			var picture = colData.pic;
			var numberFormat = "";
			var integerPart = "0"
			if (value === 0 && picture.match(blankWhenZeroRegex)) {
				return "";
			}
			if (picture.indexOf(gx.thousandSeparator) >= 0) {
				integerPart += ",000";
			}
			if (colData.dec > 0)
				numberFormat = integerPart + "." + (extUtilFormat.leftPad("", colData.dec, '0') || '0');
			else
				numberFormat = integerPart;

			extUtilFormat.thousandSeparator = gx.thousandSeparator;
			extUtilFormat.decimalSeparator = gx.decimalPoint;
			v = extUtilFormat.number(value, numberFormat);

			// Left fill with zeros if applies
			if (!pictureHelperRegex) {
				pictureHelperRegex = new RegExp("^[9" + gx.decimalPoint + gx.thousandSeparator + "]+$");
			}
			var matches = picture.match(pictureHelperRegex);
			if (matches && matches.length > 0) {
				v = picture.substr(0, picture.length - v.length).replace(/9/ig, "0") + v;
			}

			return v + "";
		},

		mapDatePictureToFormat: function (vStruct) {
			var dateFormat = function (FormatPart, Picture) {
				if (FormatPart == 'Y' && Picture.substr(0, 10) == '99/99/9999')
					return 'Y';
				else if (FormatPart == 'Y')
					return 'y';
				else if (FormatPart == 'M')
					return 'm';
				else if (FormatPart == 'D')
					return 'd';
				else return '';
			};

			var dateTimeFormat = function (Dec) {
				var timeFmt = gx.timeFormat;
				var DPTF = '', AMPM = '', TimeFmt;
				if (timeFmt == 12) {
					DPTF = 'h';
					AMPM = ' A';
				} else if (timeFmt == 24) {
					DPTF = 'H';
					AMPM = '';
				}

				if (Dec == 2)
					TimeFmt = '';
				else if (Dec == 5)
					TimeFmt = ':i';
				else if (Dec == 8)
					TimeFmt = ':i:s';
				else
					return '';

				return DPTF + TimeFmt + AMPM;
			};


			var Picture = vStruct.dp.pic,
				Dec = vStruct.dp.dec,
				Len = vStruct.len,
				dateFmt = gx.dateFormat,
				D1 = dateFmt.substr(0, 1),
				D2 = dateFmt.substr(1, 1),
				D3 = dateFmt.substr(2, 1),
				DD1 = dateFormat(D1, Picture),
				DD2 = dateFormat(D2, Picture),
				DD3 = dateFormat(D3, Picture),
				DT = dateTimeFormat(Dec);

			if (Len > 0 && Dec > 0)
				return DD1 + '/' + DD2 + '/' + DD3 + ' ' + DT;
			else if (Len > 0)
				return DD1 + '/' + DD2 + '/' + DD3;
			else
				return DT;
		},

		formatDate: function (value, vStruct) {
			var gxdate = value.gxdate;
			if (value - new Date(0, 0, 0, 0, 0, 0, 0) === 0 && gxdate) {
				var gxFormat = gxdate.SFmt,
					dp = vStruct.dp;
				if (dp && dp.pic && dp.pic.indexOf("9999") >= 0) {
					gxFormat = "Y4";
				}
				return gxdate.emptyDateString(gxFormat);
			}
			else {
				var format = this.mapDatePictureToFormat(vStruct);
				return Ext.util.Format.date(value, format);
			}
		},

		renderer: function (value, metadata, record, rowIndex, colIndex, store) {
			var col = this.gxColumn,
				gxControl = col.gxControl,
				controlTypes = gx.html.controls.types,
				v = value;

			if (col.type == gx.types.date || col.type == gx.types.dateTime) {
				v = this.formatDate(value, gxControl.vStruct);
			}

			if (col.type == gx.types.numeric && typeof (value) == "number") {
				v = this.formatNumber(value, this.gxGrid.ParentObject.GXValidFnc[this.gxColumn.gxId]);
			}

			if (record.isSummary) {
				return v;
			}

			var cell = record.raw[this.actualColIndex];

			if (gx.lang.gxBoolean(cell.visible)) {
				if (!metadata.tdCls) {
					metadata.tdCls = '';
				}

				if (gx.lang.gxBoolean(this.gxGrid.UseThemeClasses) && cell.cssClass) {
					metadata.tdCls += ' ' + cell.cssClass;
				}

				if (this.gxGrid.isCellEditable(cell) && this.gxGrid.EditableCellClass) {
					metadata.tdCls += ' ' + this.gxGrid.EditableCellClass;
				}

				if (cell.link) {
					v = Ext.String.format('<a href="{0}" alt="{2}" target="{3}">{1}</a>', cell.link || "", v || "", cell.alt || "", cell.linkTarget || "");
				}

				var style = "";
				if (cell.style)
					style += this.extractCssProperties(["text-decoration", "color", "background-color", "font-weight"], cell.style);

				// If the cell fires a user event and is enabled, wrap with an anchor tag.
				if (cell.grid.grid.isCellEventEnabled(cell)) {
					style += 'cursor:pointer;';
				}

				if (style)
					metadata.style = style;

				//Show Tooltip text if set
				if (cell.title) {
					v = Ext.String.format("<span data-qtip='{0}'>{1}</span>", cell.title, v);
				}
				return v;
			}
			return "";
		},

		extractCssProperties: function (properties, inputStyle) {
			var buffer = [];
			for (var i = 0, len = properties.length; i < len; i++) {
				var propMatch = inputStyle.match(new RegExp(properties[i] + ":([^;]+);?"));
				if (propMatch)
					buffer.push(Ext.String.format(properties[i] + ":{0};", propMatch[1]));
			}
			return buffer.join("");
		},

		getEditorPlugin: function () {
			return this.gxGrid.getEditorPlugin();
		}
	};
} ());

Ext.define('gxui.GridExtension.ImageColumn', {
	extend: 'gxui.GridExtension.Column',
	alias: 'widget.gxui.imagecolumn',

	defineEditor: Ext.emptyFn,

	renderer: function (value, metadata, record, rowIndex, colIndex, store) {
		var cell = record.raw[this.actualColIndex];
		if (gx.lang.gxBoolean(cell.visible)) {
			var styleBuffer = [];
			if (cell.width) {
				styleBuffer.push("width:");
				styleBuffer.push(cell.width);
				styleBuffer.push(cell.widthUnit);
				styleBuffer.push(";");
			}
			if (cell.height) {
				styleBuffer.push("height:");
				styleBuffer.push(cell.height);
				styleBuffer.push(cell.heightUnit);
				styleBuffer.push(";");
			}
			value = Ext.String.format('<img src="{0}" class="{1}" title="{2}" style="{3}"/>', cell.value, cell.cssClass, cell.title, styleBuffer.join(""));
		}
		return this.callParent([value, metadata, record, rowIndex, colIndex, store]);
	}
});

Ext.define('gxui.GridExtension.BlobColumn', {
	extend: 'gxui.GridExtension.Column',
	alias: 'widget.gxui.blobcolumn',

	defineEditor: Ext.emptyFn,

	renderer: function (value, metadata, record, rowIndex, colIndex, store) {
		var cell = record.raw[this.actualColIndex],
			gxControl = cell.column.gxControl,
			imgType = (gxControl.contentType.toLowerCase().indexOf('image/') != -1),
			url = cell.url;

		if (gx.lang.gxBoolean(cell.visible)) {
			if (gxControl.display === 0 && imgType) {
				if (!url || url == gx.util.resourceUrl(gx.basePath + gx.staticDirectory)) {
					url = gx.ajax.getImageUrl(gx, 'blankImage');
				}
				value = Ext.String.format('<img src="{0}" border="0" class="gxui-gridcell-blob {1}"/>', url, cell.cssClass);
			}
			else {
				value = Ext.String.format('<img src="{0}" border="0" class="{1}"/>', gx.ajax.getImageUrl(gx, 'downloadImage'), cell.cssClass);
			}
			cell.link = url;
			cell.linkTarget = "_blank";
		}
		return this.callParent([value, metadata, record, rowIndex, colIndex, store]);
	}
});

Ext.define('gxui.GridExtension.CheckColumn', {
	extend: 'Ext.ux.CheckColumn',
	alias: 'widget.gxui.checkcolumn',

	processEvent: function (type, view, cell, recordIndex, cellIndex, e) {
		if (type == 'mousedown' || (type == 'keydown' && (e.getKey() == e.ENTER || e.getKey() == e.SPACE))) {
			var record = view.panel.store.getAt(recordIndex),
				cell = record.raw[this.actualColIndex];
			if (cell.enabled)
				return this.callParent(arguments);
		}
		else {
			return this.callParent(arguments);
		}
	},
	listeners: {
		'checkchange': function (column, rowIndex, checked) {
			var grid = column.ownerCt.ownerCt,
				editorPlugin = grid.getPlugin(grid.id + '-celledit'),
				editingContext = editorPlugin.getEditingContext(rowIndex, column);

			if (editorPlugin)
				editorPlugin.fireEvent('edit', this, editingContext);

			grid.fireEvent('cellclick', grid.getView(), null, editingContext.colIdx, editingContext.record, null, editingContext.rowIdx, editingContext);
		}
	}
});

Ext.define('gxui.GridExtension.RadioColumn', {
	extend: 'gxui.GridExtension.Column',
	alias: 'widget.gxui.radiocolumn',

	defineEditor: Ext.emptyFn,

	renderer: function (value, metadata, record, rowIndex, colIndex, store) {
		var cell = record.raw[this.actualColIndex];
		if (gx.lang.gxBoolean(cell.visible)) {
			if (typeof value == "string") {
				value = value.trim();
			}
			value = gx.fn.selectedDescription({ s: value, v: cell.possibleValues });
		}
		return this.callParent([value, metadata, record, rowIndex, colIndex, store]);
	}
});

Ext.define('gxui.GridExtension.ComboColumn', {
	extend: 'gxui.GridExtension.Column',
	alias: 'widget.gxui.combocolumn',

	defineEditor: function (gxColumn, actualColIndex) {
		var vStruct = gxColumn.gxControl.vStruct,
			isSuggest = vStruct && vStruct.gxsgprm && this.gxGrid.requestSuggest;

		return {
			xtype: 'combobox',
			editable: isSuggest,
			triggerAction: 'all',
			selectOnFocus: true,
			disableKeyFilter: false,
			forceSelection: true,
			store: [["", ""]],
			queryMode: 'local',
			typeAhead: isSuggest ? vStruct.gxsgprm[3] : false,
			getActiveRecord: function (column) {
				var plugin = column.getEditorPlugin();
				if (column.gxGrid.EditModel == 'CellEditModel')
					return plugin.getActiveRecord();
				return plugin.context.record;
			},
			populateCombo: function (column, query) {
				var record = this.getActiveRecord(column),
					cell = record.raw[column.actualColIndex],
					gxGrid = column.gxGrid;

				if (isSuggest) {
					query = query || "";
					gxGrid.requestSuggest(column.actualColIndex, cell.row.id, query).done(Ext.bind(function (data) {
						this.getStore().loadData(Ext.Array.map(data, function (item) {
							return [item.c, item.d];
						}));
					}, this));
				}
				else {
					this.getStore().loadData(cell.possibleValues);
					if (typeof cell.value == "string")
						this.select(cell.value.trim());
					else
						this.select(cell.value);
				}
			},
			listeners: {
				'beforerender': function (combo) {
					if (this.gxGrid.EditModel == 'CellEditModel') {
						combo.populateCombo(this);
					}
					else {
						combo.ownerCt.on('show', Ext.bind(combo.populateCombo, combo, [this]));
					}
				},
				'beforequery': function (queryEvent) {
					queryEvent.combo.populateCombo(this, queryEvent.query || queryEvent.combo.rawValue);
				},
				'select': function () {
					if (this.gxGrid.EditModel == 'CellEditModel') {
						this.getEditorPlugin().completeEdit();
					}
				},
				scope: this
			}
		};
	},

	renderer: function (value, metadata, record, rowIndex, colIndex, store) {
		var cell = record.raw[this.actualColIndex];
		if (gx.lang.gxBoolean(cell.visible)) {
			if (!cell.vStruct || !cell.vStruct.gxsgprm || !this.gxGrid.requestSuggest) {
				if (typeof cell.value == "string") {
					value = value + "";
				}
				value = gx.fn.selectedDescription({ s: value, v: cell.possibleValues });
			}
		}
		return this.callParent([value, metadata, record, rowIndex, colIndex, store]);
	}
});

gxui.GridExtension.ColumnRenderers = function () {
	var GE = gxui.GridExtension;
	var types = gx.html.controls.types;

	var renderers = {};
	renderers[types.image] = 'gxui.imagecolumn';
	renderers[types.checkBox] = 'gxui.checkcolumn';
	renderers[types.radio] = 'gxui.radiocolumn';
	renderers[types.comboBox] = 'gxui.combocolumn';
	renderers[types.blob] = 'gxui.blobcolumn'

	renderers.get = function (col) {
		var t = col.gxControl.type,
			vStruct = col.gxControl.vStruct;

		if (vStruct && t == types.singleLineEdit && vStruct.gxsgprm && col.gxControl.grid.grid.requestSuggest) {
			return renderers[types.comboBox];
		}

		if (this[t]) {
			return this[t];
		}

		return 'gxui.column';
	};

	return renderers;
} ();  

/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.TabPanel', {
	extend: 'gxui.UserControl',

	initialize: function () {
		this.callParent(arguments);

		this.HandleUniqueId = true;
	},

	//Private members
	m_tabPanel: null,
	m_designTabs: [],
	m_activeTab: 0,

	// Databinding for property Data
	SetRunTimeTabs: function (data) {
		if (data) {
			if (Ext.isArray(data))
				this.RunTimeTabs = data;
			else
				this.RunTimeTabs = [data];
		}
		else
			this.RunTimeTabs = [];
	},

	// Databinding for property Data
	GetRunTimeTabs: function () {
		return this.RunTimeTabs;
	},

	onRender: function () {
		this.m_designTabs = Ext.JSON.decode(this.DesignTimeTabs);
		this.RunTimeTabs = [];

		var tabCount = 0;
		if (this.m_designTabs && this.m_designTabs.length)
			tabCount = this.m_designTabs.length;

		if (tabCount > 0) {
			this.displayTabPanels();
			this.m_tabPanel = Ext.create('Ext.tab.Panel', this.getConfig());

			// Register this User Control as a container. Each tab of the tabpanel control is registered
			// as an individual container.
			this.registerAsContainer();
		}
	},

	onRefresh: function () {
		var setActiveTab = false;
		Ext.each(this.getTabPanelsList(), function (tab, index, allTabs) {
			this.m_tabPanel.add(tab);
			this.registerAsContainer(tab);
			setActiveTab = true;
		}, this);

		if (setActiveTab) {
			this.m_tabPanel.setActiveTab(this.m_activeTab);
		}
	},

	onAfterRender: function () {
		this.m_tabPanel.setActiveTab(this.m_activeTab);
		this.m_tabPanel.on('tabchange', this.handlers.tabChanged, this);
	},

	onDestroy: function () {
		if (this.m_tabPanel) {
			this.m_tabPanel.items.each(function (tab) {
				this.unregisterCt(tab);
			}, this);
		}
		this.callParent(arguments);
	},

	getUnderlyingControl: function () {
		return this.m_tabPanel;
	},

	addToParent: function () {
		return gxui.CBoolean(this.AddToParentGxUIControl);
	},

	getConfig: function () {
		var config = {
			id: this.getUniqueId(),
			cls: this.Cls,
			tabPosition: this.TabPosition || "top",
			deferredRender: false,
			border: gx.lang.gxBoolean(this.Border) ? undefined : 0,
			frame: gx.lang.gxBoolean(this.Frame),
			autoWidth: gxui.CBoolean(this.AutoWidth),
			autoHeight: gxui.CBoolean(this.AutoHeight),
			enableTabScroll: (this.TabPosition == "top") ? gxui.CBoolean(this.EnableTabScroll) : false,
			minTabWidth: parseInt(this.MinTabWidth),
			items: this.getTabPanelsList(),
			listeners: {
				'activate': this.handlers.tabItemActivated,
				'deactivate': this.handlers.tabItemDeactivated,
				'remove': this.handlers.tabItemClosed,
				'beforeremove': this.handlers.tabItemBeforeClosed,
				'afterrender': this.fixAutoDimensions,
				'add': this.fixAutoDimensions,
				scope: this
			}
		};

		if (!gxui.CBoolean(this.AutoWidth))
			config.width = parseInt(this.Width);

		if (!gxui.CBoolean(this.AutoHeight))
			config.height = parseInt(this.Height);

		return config;
	},

	getTabPanelsList: function () {
		var rawTabs = (this.RunTimeTabs && this.RunTimeTabs.length) ? this.m_designTabs.concat(this.RunTimeTabs) : this.m_designTabs;
		var tabPanels = [];
		Ext.each(rawTabs, function (tab, index, allTabs) {
			var panel;
			if (index >= this.m_designTabs.length)
				tab.isRuntimeTab = true;

			if (!tab.rendered) {
				if (!tab.isRuntimeTab) {
					var titleEl = Ext.get(this.getChildContainer("Title" + tab.id));
					if (titleEl) {
						tab.Name = titleEl.dom.innerHTML;
						titleEl.dom.parentNode.removeChild(titleEl.dom);
					}
				}

				if (tab.isRuntimeTab) {
					if (!tab.HTML) {
						tab.InternalName = null;
					}
				}
				else {
					tab.InternalName = tab.id;
				}

				if (!tab.HTML) {
					Ext.fly(this.getChildContainer(tab.id)).setStyle('display', 'inline-block');
				}

				if (tab.InternalName) {
					var layout = tab.layout || this.Layout;
					var config = {
						id: this.getTabUniqueId(tab.InternalName),
						layout: layout == "default" ? undefined : layout,
						contentEl: !tab.HTML ? this.getChildContainer(tab.id) : undefined,
						bodyCls: "gxui-noreset",
						html: tab.HTML,
						title: tab.Name,
						closable: (tab.isRuntimeTab) ? (tab.closable !== undefined ? gxui.CBoolean(tab.closable) : true) : gxui.CBoolean(tab.closable),
						autoScroll: tab.autoScroll || (layout == 'fit' ? false : true),
						autoWidth: gxui.CBoolean(this.AutoWidth),
						autoHeight: gxui.CBoolean(this.AutoHeight),
						listeners: {
							'activate': this.handlers.tabItemActivated,
							'deactivate': this.handlers.tabItemDeactivated,
							'render': this.handlers.tabItemRendered,
							'afterrender': this.fixAutoDimensions,
							'add': this.fixAutoDimensions,
							scope: this
						}
					};

					if (this.TabCls)
						config.cls = this.TabCls;

					// WA to support AutoHeight
					if (gxui.CBoolean(this.AutoHeight))
						if (!tab.HTML)
							Ext.fly(this.getChildContainer(tab.id)).setHeight('auto');

					panel = Ext.create('Ext.panel.Panel', config);
					tab.rendered = true;
					tabPanels.push(panel);
				}
				else
					return;
			}

			if (gxui.CBoolean(tab.selected) || gxui.CBoolean(tab.Selected))
				this.m_activeTab = panel;

		}, this);

		return tabPanels;
	},

	displayTabPanels: function () {
		Ext.each(this.m_designTabs, function (tab, index, allTabs) {
			Ext.get(this.getChildContainer(tab.id)).setDisplayed(true)
		}, this);
	},

	registerAsContainer: function (t) {
		if (t) {
			this.registerCt(Ext.get(t.contentEl || t.body).dom, t.add, t.doLayout, t);
		}
		else {
			Ext.each(this.m_tabPanel.items.items,
				function (tab, index, allTabs) {
					this.registerCt(Ext.get(tab.contentEl || tab.body).dom, tab.add, tab.doLayout, tab);
				},
			this);
		}
	},

	fixAutoDimensions: function (panel, onlyThisTab) {
		if (!panel.fixingAutoDims) {
			panel.fixingAutoDims = true;
			if (panel.rendered) {
				if (!onlyThisTab) {
					Ext.each(this.m_tabPanel.items.items, function (tab, index, allTabs) {
						this.fixAutoDimensions(tab, true);
					}, this);
				}

				if (gxui.CBoolean(this.AutoWidth)) {
					panel.el.setWidth('auto');
					panel.body.setWidth('auto');
					if (panel.header && (panel.headerPosition == "top" || panel.headerPosition == "bottom")) {
						Ext.defer(panel.header.setWidth, 50, panel.header, ['auto']);
					}
				}

				if (gxui.CBoolean(this.AutoHeight)) {
					panel.el.setHeight('auto');
					panel.body.setHeight('auto');
					if (panel.header && (panel.headerPosition == "top" || panel.headerPosition == "bottom")) {
						panel.body.setStyle('margin-bottom', Ext.dom.AbstractElement.addUnits(panel.header.getHeight(), "px"));
					}
				}
			}

			if (panel == this.m_tabPanel) {
				panel.getTabBar().doLayout();
			}
			panel.fixingAutoDims = false;
		}
	},

	handlers: {
		tabChanged: function (tab, tabItem) {
			
			if (this.TabChanged) {
				this.TabChanged();
			}
		},

		tabItemRendered: function (panel) {
			panel.tab.on('click', this.handlers.tabStripClick, this);
		},

		tabItemActivated: function (tabItem) {
			if (gxui.CBoolean(this.AutoHeight) || gxui.CBoolean(this.AutoWidth)) {
				tabItem.el.select('.x-panel').each(function (panelEl) {
					var innerPanel = Ext.getCmp(panelEl.dom.id);
					var initialConfig = innerPanel.initialConfig;
					if (!innerPanel.ownerCt && (initialConfig.autoWidth || initialConfig.autoHeight)) {
						innerPanel.doLayout();
					}
				});
				Ext.defer(tabItem.ownerCt.doLayout, 50, tabItem.ownerCt);
			}
			this.ActiveTabId = tabItem.id;
			if (this.RunTimeTabs)
				Ext.each(this.RunTimeTabs, function (item, index, allItems) {
					if (this.getTabUniqueId(item.InternalName) == tabItem.id) {
						item.Selected = true;
						return false;
					}
				}, this);
		},

		tabItemDeactivated: function (tabItem) {
			if (this.RunTimeTabs)
				Ext.each(this.RunTimeTabs, function (item, index, allItems) {
					if (this.getTabUniqueId(item.InternalName) == tabItem.id) {
						item.Selected = false;
						return false;
					}
				}, this);
		},

		tabItemClosed: function (tabPanel, tabItem) {
			if (Ext.getClassName(tabPanel) == "Ext.tab.Panel") {
				if (this.RunTimeTabs) {
					var rtt = [];
					Ext.each(this.RunTimeTabs, function (tab, index, allTabs) {
						if (this.getTabUniqueId(tab.InternalName) != tabItem.id) {
							rtt.push(tab);
						}
					}, this);
					this.SetRunTimeTabs(rtt);
				}
				
				if (this.TabClosed) {
					this.ClosedTabId = tabItem.id;
					this.TabClosed();
				}
			}
		},

		tabItemBeforeClosed: function (tabPanel, tabItem) {
			if (Ext.getClassName(tabPanel) == "Ext.tab.Panel") {
				
				if (this.BeforeTabClosed) {
					this.ClosedTabId = tabItem.id;
					this.CancelEvent = false;
					this.BeforeTabClosed();
					return !this.CancelEvent;
				}
			}
		},

		tabStripClick: function (tab, e) {
			
			if (this.TabClick) {
				this.ActiveTabId = tab.card.id;
				this.TabClick();
			}
		}
	},

	getTabUniqueId: function (tabId) {
		if (gxui.CBoolean(this.HandleUniqueId))
			return this.getUniqueId() + "_tab_" + tabId;
		else
			return tabId;
	},

	methods: {
		// Methods
		
		OpenTab: function (tabId, title, tabHTMLContent, closable, layout) {
			if (this.IsTabOpen(tabId)) {
				this.m_activeTab = this.getTabUniqueId(tabId);
			}
			else {
				var tab = {
					Name: title,
					InternalName: tabId,
					HTML: tabHTMLContent,
					Selected: true,
					closable: closable
				};
				if (layout)
					tab.layout = layout;
				this.RunTimeTabs.push(tab);

				Ext.each(this.getTabPanelsList(), function (tab, index, allTabs) {
					var tabPanel = this.m_tabPanel;
					tabPanel.add(tab);
					tabPanel.doLayout();
					this.registerAsContainer(tab);
				}, this);
			}

			this.m_tabPanel.setActiveTab(this.m_activeTab);
		},

		
		CloseTab: function (tabId) {
			var tabPanel = this.m_tabPanel;
			if (this.IsTabOpen(tabId)) {
				var tab = tabPanel.child("#" + this.getTabUniqueId(tabId));
				if (tab) {
					tabPanel.remove(tab, true);
				}
			}
		},

		
		SelectTab: function (tabId) {
			this.m_activeTab = this.getTabUniqueId(tabId);
			this.m_tabPanel.setActiveTab(this.m_activeTab);
		},

		
		IsTabOpen: function (tabId) {
			var tab = this.m_tabPanel.child("#" + this.getTabUniqueId(tabId));
			return (tab) ? true : false;
		},

		
		ShowTab: function (i) {
			var panel = this.m_tabPanel.items.get(i);
			if (panel)
				panel.tab.show();
		},

		
		HideTab: function (i) {
			var panel = this.m_tabPanel.items.get(i);
			if (panel)
				panel.tab.hide();
		},

		
		ShowTabById: function (tabId) {
			var panel = this.m_tabPanel.child("#" + this.getTabUniqueId(tabId));
			if (panel)
				panel.tab.show();
		},

		
		HideTabById: function (tabId) {
			var panel = this.m_tabPanel.child("#" + this.getTabUniqueId(tabId));
			if (panel)
				panel.tab.hide();
		},

		
		SetTabDirty: function (tabId, dirty) {
			var tab = this.m_tabPanel.child("#" + this.getTabUniqueId(tabId));
			if (tab) {
				tab.dirty = dirty;
				var tabTextEl = tab.tab.btnInnerEl
				if (tabTextEl) {
					if (Ext.isIE) {
						var tabTextHtmlEl = tabTextEl.dom;
						if (dirty) {

							tabTextHtmlEl.innerHTML += "*";
						}
						else {
							if (tabTextHtmlEl.innerHTML.charAt(tabTextHtmlEl.innerHTML.length - 1) == "*")
								tabTextHtmlEl.innerHTML = tabTextHtmlEl.innerHTML.substring(0, tabTextHtmlEl.innerHTML.length - 1);
						}
					}
					else {
						if (dirty) {
							tabTextEl.addCls("x-tab-strip-dirty");
						}
						else {
							tabTextEl.removeCls("x-tab-strip-dirty");
						}
					}
				}
			}
		},

		
		IsTabDirty: function (tabId) {
			var tab = this.m_tabPanel.child("#" + this.getTabUniqueId(tabId));
			return tab && (tab.dirty == true);
		},

		
		SetTabTitle: function (tabId, title) {
			var tab = this.m_tabPanel.child("#" + this.getTabUniqueId(tabId));
			if (tab) {
				tab.setTitle(title);
				this.SetTabDirty(tabId, tab.dirty || false);
			}
		}
	}
});
/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.Message', function () {

	// Private variables
	var msgCt;

	return {
		extend: 'gxui.UserControl',

		initialize: function () {
			this.callParent(arguments);
		},

		onRender: function () {
			if (gxui.CBoolean(this.Show)) {
				this.ShowMessage();
			}
		},

		onRefresh: function () {
			if (gxui.CBoolean(this.Show)) {
				this.ShowMessage();
			}
		},

		createBox: function (t, s) {
			return ['<div class="msg">',
	                '<div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>',
	                '<div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc"><h3>', t, '</h3>', s, '</div></div></div>',
	                '<div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>',
	                '</div>'].join('');
		},

		methods: {
			
			ShowMessage: function (title, message, type) {
				type = type || this.Type;
				message = message || this.Message;
				title = title || this.Title;

				if (type == "alert") {
					var msgBox = Ext.create('Ext.window.MessageBox');
					Ext.defer(msgBox.show, 100, msgBox, [{
						title: title,
						msg: message,
						buttons: Ext.MessageBox.OK,
						icon: this.Icon == "info" ? Ext.MessageBox.INFO : (this.Icon == "question" ? Ext.MessageBox.QUESTION : (this.Icon == "warning" ? Ext.MessageBox.WARNING : Ext.MessageBox.ERROR))
					}]);
				}
				else {
					var titleMsgs = (title || "").split("|");
					Ext.each((message || "").split("|"), function (msg, i) {
						// Create the message box
						if (!msgCt) {
							msgCt = Ext.DomHelper.insertFirst(document.body, { id: 'msg-div' }, true);
							if (this.Cls != "")
								msgCt.addClass(this.Cls);
						}
						msgCt.alignTo(document, this.Position + '-' + this.Position);
						var m = Ext.DomHelper.append(msgCt, { html: this.createBox(titleMsgs[i], msg) }, true);

						var timeoutId;
						var hideMessage = function () {
							var f;
							if (this.Position == 'c') {
								f = function () {
									m.fadeOut({
										opacity: 0, //can be any value between 0 and 1 (e.g. .5)
										easing: 'easeOut',
										remove: true
									});
								};
							} else {
								f = Ext.bind(function () {
									m.ghost(this.Position, { remove: true });
								}, this);
							}
							timeoutId = setTimeout(f, this.Duration * 1000);
						};

						// Slide the message box into view
						m.slideIn(this.Position, {
							callback: hideMessage,
							scope: this
						});

						// Do not hide the message box if the mouse is over it
						m.on('mouseover', function (e) {
							if (timeoutId) {
								clearTimeout(timeoutId);
								timeoutId = null;
							}
						}, this);

						// If the mouse is outside the message box, schedule its hiding, according to duration
						m.on('mouseout', function (e) {
							var box = m.getBox();
							var x = e.getPageX();
							var y = e.getPageY();
							if (x && y) {
								if (x > box.x && x < box.x + box.width && y > box.y && y < box.y + box.height) {
									return
								}
							}
							Ext.bind(hideMessage, this)();
						}, this)
					}, this);
				}
			}
		}
	};
} ());
/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.Menu', {
	extend: 'gxui.UserControl',

	initialize: function () {
		this.callParent(arguments);
		this.unmanagedLayout = true;
	},

	// Databindings
	SetMenu: function (data) {
		this.Menu = data;
	},

	// Databindings
	GetMenu: function (data) {
		return this.Menu;
	},

	onRender: function () {
		this.m_menu = this.createMenu(this.Menu);
	},

	onRefresh: function () {
	},

	getUnderlyingControl: function () {
		return this.m_menu;
	},

	// Overriden
	runDeferredMethod: function () {
		return !!this.getUnderlyingControl();
	},

	createMenu: function (menu) {
		if (menu) {
			return new Ext.menu.Menu({
				items: this.getContextMenuItems(menu),
				ignoreParentClicks: true
			});
		}
	},

	getContextMenuItems: function (contextMenu) {
		var cmItems = [];

		if (contextMenu) {
			Ext.each(contextMenu, function (item) {
				var config;
				switch (item.Type) {
					case 'Text':
						config = item.Text;
						break;
					case 'Separator':
						config = '-';
						break;
					case 'Menu':
						config = this.getBasicItemConfig(item);
						config.menu = this.getContextMenuItems(item.Items);
						delete config.handler;
						break;
					default:
						config = this.getBasicItemConfig(item);
						break;
				}

				cmItems.push(config);
			}, this);
		}

		return cmItems;
	},

	getBasicItemConfig: function (item) {
		return {
			gxid: item.Id,
			text: item.Text,
			tooltip: item.Tooltip,
			icon: item.Icon,
			iconCls: item.IconCls,
			cls: (item.Cls != "") ? item.Cls : (item.Text != "") ? "x-btn-text-icon" : "x-btn-icon",
			disabled: item.Disabled,
			hidden: item.Hidden,
			handler: this.itemClickHandler,
			scope: this
		};
	},

	itemClickHandler: function (btn) {
		
		if (this.ItemClick) {
			this.ItemClickedId = btn.gxid;
			this.ItemClick();
		}
	},

	methods: {
		// Methods
		
		ShowMenu: function (m, x, y) {
			var menu = this.m_menu;
			if (m) {
				menu = this.createMenu(m);
			}
			var xy = (x && y) ? [x, y] : Ext.EventObject.getXY();
			if (menu) {
				menu.showAt(xy);
			}
		},

		
		ShowMenuXY: function (x, y) {
			this.ShowMenu(this.Menu, x, y);
		}
	}
});

// Supported item types
gxui.Menu.ItemType = {
	Button: "Button",
	Text: "Text",
	Separator: "Separator",
	Menu: "Menu"
};

/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.Viewport', {
	extend: 'gxui.UserControl',

	initialize: function () {
		this.callParent(arguments);
	},

	onRender: function () {
		var body = this.getChildContainer("Body");
		this.m_panel = Ext.create('gxui.container.NestedViewport', {
			id: this.getUniqueId(),
			contentEl: body,
			bodyCls: "gxui-noreset",
			layout: 'fit',
			renderTo: 'MAINFORM'
		});

		Ext.fly(body).setDisplayed(true);

		// Register as UC Container
		this.registerCt(this.getChildContainer("Body"), this.m_panel.add, this.m_panel.doLayout, this.m_panel);
	},

	getUnderlyingControl: function () {
		return this.m_panel;
	}
});
// vim: ts=4:sw=4:nu:fdc=2:nospell


Ext.define('Ext.ux.state.HttpProvider', {
	extend: 'Ext.state.Provider'

	
    , async: true
	
    , flushCache: false
	// localizable texts
    , saveSuccessText: 'Save Success'
    , saveFailureText: 'Save Failure'
    , readSuccessText: 'Read Success'
    , readFailureText: 'Read Failure'
    , dataErrorText: 'Data Error'

	//private
    , constructor: function (config) {
    	this.addEvents(
    	
             'readsuccess'
    	
            , 'readfailure'
    	
            , 'savesuccess'
    	
            , 'savefailure'
        );

    	this.callParent(arguments);

    	Ext.apply(this, config, {
    		// defaults
    		delay: 750 // buffer changes for 750 ms
            , dirty: false
            , started: false
            , autoStart: true
            , autoRead: true
            , user: 'user'
            , id: 1
            , session: 'session'
            , logFailure: false
            , logSuccess: false
            , queue: []
            , url: '.'
            , readUrl: undefined
            , saveUrl: undefined
            , method: 'POST'
            , saveBaseParams: {}
            , readBaseParams: {}
            , paramNames: {
            	id: 'id'
                , name: 'name'
                , value: 'value'
                , user: 'user'
                , session: 'session'
                , data: 'data'
            }
    	}); // eo apply

    	if (this.autoRead) {
    		this.readState();
    	}

    	this.dt = Ext.create('Ext.util.DelayedTask', this.submitState, this);
    	if (this.autoStart) {
    		this.start();
    	}
    } //eof constructor


	// {{{
	
    , initState: function (state) {
    	if (state instanceof Array) {
    		Ext.each(state, function (item) {
    			this.state[item.name] = this.decodeValue(item[this.paramNames.value]);
    		}, this);
    	}
    	else {
    		this.state = state ? state : {};
    	}
    } // eo function initState
	// }}}
	// {{{
	
    , set: function (name, value) {
    	if (!name) { return; }


    	this.queueChange(name, value);
    } // eo function set
	// }}}
	// {{{
	
    , start: function () {
    	this.dt.delay(this.delay);
    	this.started = true;
    } // eo function start
	// }}}
	// {{{
	
    , stop: function () {
    	this.dt.cancel();
    	this.started = false;
    } // eo function stop
	// }}}
	// {{{
	
    , queueChange: function (name, value) {
    	var o = {}
            , i = 0
            , found = false
    	// see http://extjs.com/forum/showthread.php?p=344233
            , oldValue = this.state[name]
            , newValue
            , changed;

    	for (; i < this.queue.length; i++) {
    		if (this.queue[i].name === name) {
    			oldValue = this.decodeValue(this.queue[i].value);
    		}
    	}
    	//changed = undefined === oldValue || oldValue !== value;
    	//http://www.sencha.com/forum/showthread.php?24970-Buffering-Http-State-Provider&p=581091&viewfull=1#post581091
    	changed = undefined === oldValue || this.encodeValue(oldValue) !== this.encodeValue(value);

    	if (changed) {
    		newValue = this.encodeValue(value);
    		o[this.paramNames.name] = name;
    		o[this.paramNames.value] = newValue;
    		for (i = 0; i < this.queue.length; i++) {
    			if (this.queue[i].name === o.name) {
    				this.queue[i] = o;
    				found = true;
    			}
    		}
    		if (false === found) {
    			this.queue.push(o);
    		}
    		this.dirty = true;
    	}
    	if (this.started) {
    		this.start();
    	}
    	return changed;
    } // eo function bufferChange
	// }}}
	// {{{
	
    , submitState: function () {
    	if (!this.dirty || Ext.isEmpty(this.queue)) {
    		this.dt.delay(this.delay);
    		return;
    	}
    	this.dt.cancel();

    	var o = {
    		url: this.saveUrl || this.url
            , method: this.method
            , scope: this
            , success: this.onSaveSuccess
            , failure: this.onSaveFailure
    		//,queue:Ext.ux.util.clone(this.queue)
            , queueCopy: Ext.Array.clone(this.queue) //don't use 'queue', conflicts with ext-basex QueueManager 
            , params: {}
    	};

    	var params = Ext.apply({}, this.saveBaseParams);
    	params[this.paramNames.id] = this.id;
    	params[this.paramNames.user] = this.user;
    	params[this.paramNames.session] = this.session;
    	params[this.paramNames.data] = Ext.encode(o.queueCopy);

    	Ext.apply(o.params, params);

    	// be optimistic
    	this.dirty = false;

    	Ext.Ajax.request(o);
    } // eo function submitState
	// }}}
	// {{{
	
    , clear: function (name) {
    	this.set(name, undefined);
    } // eo function clear
	// }}}
	// {{{
	
    , onSaveSuccess: function (response, options) {
    	var o = {};
    	try { o = Ext.decode(response.responseText); }
    	catch (e) {
    		if (true === this.logFailure) {
    			this.log(this.saveFailureText, e, response);
    		}
    		this.dirty = true;
    		return;
    	}
    	if (true !== o.success) {
    		if (true === this.logFailure) {
    			this.log(this.saveFailureText, o, response);
    		}
    		this.dirty = true;
    	}
    	else {
    		Ext.each(options.queueCopy, function (item) {
    			if (!item) {
    				return;
    			}
    			var name = item[this.paramNames.name];
    			var value = this.decodeValue(item[this.paramNames.value]);

    			if (undefined === value || null === value) {
    				Ext.ux.state.HttpProvider.superclass.clear.call(this, name);
    			}
    			else {
    				// parent sets value and fires event
    				Ext.ux.state.HttpProvider.superclass.set.call(this, name, value);
    			}
    		}, this);
    		if (false === this.dirty) {
    			this.queue = [];
    		}
    		else {
    			var i, j, found;
    			for (i = 0; i < options.queueCopy.length; i++) {
    				found = false;
    				for (j = 0; j < this.queue.length; j++) {
    					if (options.queueCopy[i].name === this.queue[j].name) {
    						found = true;
    						break;
    					}
    				}
    				if (true === found && this.encodeValue(options.queueCopy[i].value) === this.encodeValue(this.queue[j].value)) {
    					Ext.Array.remove(this.queue, this.queue[j]);
    				}
    			}
    		}
    		if (true === this.logSuccess) {
    			this.log(this.saveSuccessText, o, response);
    		}
    		this.fireEvent('savesuccess', this);
    	}
    } // eo function onSaveSuccess
	// }}}
	// {{{
	
    , onSaveFailure: function (response, options) {
    	if (true === this.logFailure) {
    		this.log(this.saveFailureText, response);
    	}
    	this.dirty = true;
    	this.fireEvent('savefailure', this);
    } // eo function onSaveFailure
	// }}}
	// {{{
	
    , onReadFailure: function (response, options) {
    	if (true === this.logFailure) {
    		this.log(this.readFailureText, response);
    	}
    	this.fireEvent('readfailure', this);

    } // eo function onReadFailure
	// }}}
	// {{{
	
    , onReadSuccess: function (response, options) {
    	var o = {}, data;
    	try { o = Ext.decode(response.responseText); }
    	catch (e) {
    		if (true === this.logFailure) {
    			this.log(this.readFailureText, e, response);
    		}
    		return;
    	}
    	if (true !== o.success) {
    		if (true === this.logFailure) {
    			this.log(this.readFailureText, o, response);
    		}
    	}
    	else {
    		data = o[this.paramNames.data];
    		if (!(data instanceof Array) && true === this.logFailure) {
    			this.log(this.dataErrorText, data, response);
    			return;
    		}
    		//flush cache if not appending
    		if (this.flushCache) {
    			this.state = {};
    		}
    		Ext.each(data, function (item) {
    			this.state[item[this.paramNames.name]] = this.decodeValue(item[this.paramNames.value]);
    		}, this);
    		this.queue = [];
    		this.dirty = false;
    		if (true === this.logSuccess) {
    			this.log(this.readSuccessText, data, response);
    		}
    		this.fireEvent('readsuccess', this);
    	}
    } // eo function onReadSuccess
	// }}}
	// {{{
	
    , readState: function () {
    	var o = {
    		url: this.readUrl || this.url
            , method: this.method
            , scope: this
            , success: this.onReadSuccess
            , failure: this.onReadFailure
            , params: {}
            , async: this.async
    	};

    	var params = Ext.apply({}, this.readBaseParams);
    	params[this.paramNames.id] = this.id;
    	params[this.paramNames.user] = this.user;
    	params[this.paramNames.session] = this.session;

    	Ext.apply(o.params, params);
    	Ext.Ajax.request(o);
    } // eo function readState
	// }}}
	// {{{
	
    , log: function () {
    	if (console) {
    		console.log.apply(console, arguments);
    	}
    } // eo log
	// }}}

}); // eo extend

// eof
/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.Settings', {
	extend: 'gxui.UserControl',

	initialize: function () {
		this.callParent(arguments);
	},

	// Databinding
	SetState: function (data) {
		this.State = data;
	},

	// Databinding
	GetState: function (data) {
		return this.State;
	},

	onRender: function () {
		gxui.fixPaddingReset = gxui.CBoolean(this.RevertTablePaddingReset);
		gxui.fixSpacingReset = gxui.CBoolean(this.RevertTableSpacingReset);

		var provider = null;
		if (gxui.CBoolean(this.Enable)) {
			if (this.Provider == gxui.Settings.StateProvider.HTTP) {
				if (this.SaveURL != "") {
					provider = Ext.create('Ext.ux.state.HttpProvider', {
						saveUrl: this.SaveURL,
						autoRead: false
						
					});
				}
			}
			else {
				if (this.Provider == gxui.Settings.StateProvider.Cookie) {
					provider = Ext.create('Ext.state.CookieProvider', {
						expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365)) //365 days
					})
				}
			}
		}
		else {
			// This is equivalent to removing the default provider set in gxui.js.
			provider = Ext.create('Ext.state.Provider');
		}

		if (provider) {
			// Initialize state provider (required to be able to keep state in controls)
			Ext.state.Manager.setProvider(provider);
			if (this.Provider == gxui.Settings.StateProvider.HTTP) {
				Ext.state.Manager.getProvider().initState(this.State);
			}
			this.State = []; //Reset initial state to avoid innecessary traffic
		}
	},

	onDestroy: Ext.emptyFn,

	LOCALE_SCRIPT_ID: "ext-lang",

	methods: {
		// Methods
		
		SetLanguage: function (lang, charset) {
			var s = Ext.getDom(this.LOCALE_SCRIPT_ID);
			var src = gx.util.resourceUrl(gx.basePath + gx.staticDirectory + "Shared/ext/locale/ext-lang-" + lang + ".js", true);
			if (!s) {
				s = document.createElement("script");
				s.id = this.LOCALE_SCRIPT_ID;
				s.type = 'text/javascript';
				document.getElementsByTagName("head")[0].appendChild(s);
			}
			s.src = src;
			if (charset) {
				s.charset = charset;
			}
		},

		RemoveLanguage: function () {
			var s = Ext.getDom(this.LOCALE_SCRIPT_ID);
			if (s) {
				document.getElementsByTagName("head")[0].removeChild(s);
			}
		}
	}
});

// Supported state providers
gxui.Settings.StateProvider = {
	Cookie: "Cookie",
	HTTP: "HTTP"
};

/// <reference path="..\..\Freezer\Ext\ext-all-dev.js" />


Ext.define('gxui.Splash', {
	extend: 'gxui.UserControl',

	initialize: function () {
		this.callParent(arguments);
	},

	onRender: function () {
		var cookieId = this.getUniqueId() + '-cookie';

		if (gxui.getCookie(cookieId) == "") {
			try {
				this.m_mask = Ext.get(document.body).mask(this.Message + " ", this.Cls);
				this.m_mask.next().addCls('gxui-splash-msg');
				setTimeout(function () {
					Ext.get(document.body).unmask();
				}, this.Duration * 1000);
			}
			catch (err) { };
			gxui.setCookie(cookieId, '1', 0);
		}
	}

});